diff --git a/build.proj b/build.proj index 678289d..b9f7c38 100644 --- a/build.proj +++ b/build.proj @@ -67,6 +67,8 @@
+ +
diff --git a/giflib/giflib.vcxproj b/giflib/giflib.vcxproj index 8962535..8182f96 100644 --- a/giflib/giflib.vcxproj +++ b/giflib/giflib.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -39,27 +39,28 @@ {C13F9A1D-8E91-4239-ABF1-E0F01F6DBADB} giflib-416 + 8.1 StaticLibrary false - v120 + v140_xp StaticLibrary false - v120 + v140_xp StaticLibrary false - v120 + v140_xp StaticLibrary false - v120 + v140_xp diff --git a/include/leptonica_versionnumbers.props b/include/leptonica_versionnumbers.props index 47eb696..53e23f3 100644 --- a/include/leptonica_versionnumbers.props +++ b/include/leptonica_versionnumbers.props @@ -3,9 +3,9 @@ 416 8c - 171 - 1,71,0,0 - 1.71 + 172 + 1,72,0,0 + 1.72 143 394 128 diff --git a/leptonica_versionnumbers.props b/leptonica_versionnumbers.props deleted file mode 100644 index 0f72785..0000000 --- a/leptonica_versionnumbers.props +++ /dev/null @@ -1,42 +0,0 @@ - - - - 416 - 8c - 171 - 1,71,0,0 - 1.71 - 143 - 394 - 128 - - - <_ProjectFileVersion>11.0.60610.1 - - - - $(GIFLIB_VERSION) - - - $(LIBJPEG_VERSION) - - - $(LIBLEPT_VERSION) - - - $(LIBLEPT_VERSION_R) - - - $(LIBLEPT_NUMBER) - - - $(LIBPNG_VERSION) - - - $(LIBTIFF_VERSION) - - - $(ZLIB_VERSION) - - - \ No newline at end of file diff --git a/libjpeg/jpeg.vcxproj b/libjpeg/jpeg.vcxproj index b56833c..41992f8 100644 --- a/libjpeg/jpeg.vcxproj +++ b/libjpeg/jpeg.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -23,29 +23,30 @@ jpeg Win32Proj libjpeg-8c + 8.1 StaticLibrary - v120 + v140_xp NotSet false StaticLibrary - v120 + v140_xp NotSet false StaticLibrary - v120 + v140_xp NotSet true StaticLibrary - v120 + v140_xp NotSet true diff --git a/liblept/include/allheaders.h b/liblept/include/allheaders.h index 9d81ae8..191d469 100644 --- a/liblept/include/allheaders.h +++ b/liblept/include/allheaders.h @@ -29,7 +29,7 @@ #define LIBLEPT_MAJOR_VERSION 1 -#define LIBLEPT_MINOR_VERSION 71 +#define LIBLEPT_MINOR_VERSION 72 #include "alltypes.h" @@ -41,6 +41,7 @@ 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 ); @@ -129,7 +130,6 @@ LEPT_DLL extern l_int32 bbufferReadStream ( BBUFFER *bb, FILE *fp, l_int32 nbyte LEPT_DLL extern l_int32 bbufferExtendArray ( BBUFFER *bb, l_int32 nbytes ); LEPT_DLL extern l_int32 bbufferWrite ( BBUFFER *bb, l_uint8 *dest, size_t nbytes, size_t *pnout ); LEPT_DLL extern l_int32 bbufferWriteStream ( BBUFFER *bb, FILE *fp, size_t nbytes, size_t *pnout ); -LEPT_DLL extern l_int32 bbufferBytesToWrite ( BBUFFER *bb, size_t *pnbytes ); 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 ); @@ -178,18 +178,21 @@ LEPT_DLL extern PIX * pixMultiplyByColor ( PIX *pixd, PIX *pixs, BOX *box, l_uin 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_BMF * bmfCreate ( const char *dir, l_int32 size ); +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_int32 bmfGetWidth ( L_BMF *bmf, char chr, l_int32 *pw ); LEPT_DLL extern l_int32 bmfGetBaseline ( L_BMF *bmf, char chr, l_int32 *pbaseline ); -LEPT_DLL extern PIXA * pixaGetFont ( const char *dir, l_int32 size, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); -LEPT_DLL extern l_int32 pixaSaveFont ( const char *indir, const char *outdir, l_int32 size ); -LEPT_DLL extern PIXA * pixaGenerateFont ( const char *dir, l_int32 size, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); +LEPT_DLL extern PIXA * pixaGetFont ( const char *dir, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); +LEPT_DLL extern l_int32 pixaSaveFont ( const char *indir, const char *outdir, l_int32 fontsize ); +LEPT_DLL extern PIXA * pixaGenerateFontFromFile ( const char *dir, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); +LEPT_DLL extern PIXA * pixaGenerateFontFromString ( l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); +LEPT_DLL extern PIXA * pixaGenerateFont ( PIX *pixs, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); LEPT_DLL extern PIX * pixReadStreamBmp ( FILE *fp ); LEPT_DLL extern l_int32 pixWriteStreamBmp ( FILE *fp, PIX *pix ); LEPT_DLL extern PIX * pixReadMemBmp ( const l_uint8 *cdata, size_t size ); LEPT_DLL extern l_int32 pixWriteMemBmp ( l_uint8 **pdata, size_t *psize, PIX *pix ); +LEPT_DLL extern void * l_bootnum_gen ( ); 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 ); @@ -290,8 +293,8 @@ LEPT_DLL extern BOXA * boxaBinSort ( BOXA *boxas, l_int32 sorttype, l_int32 sort 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_int32 boxaExtractAsNuma ( BOXA *boxa, NUMA **pnax, NUMA **pnay, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid ); -LEPT_DLL extern l_int32 boxaExtractAsPta ( BOXA *boxa, PTA **pptal, PTA **pptat, PTA **pptar, PTA **pptab, l_int32 keepinvalid ); +LEPT_DLL extern l_int32 boxaExtractAsNuma ( BOXA *boxa, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid ); +LEPT_DLL extern l_int32 boxaExtractAsPta ( BOXA *boxa, PTA **pptal, PTA **pptat, PTA **pptar, PTA **pptab, PTA **pptaw, PTA **pptah, l_int32 keepinvalid ); LEPT_DLL extern BOX * boxaGetRankSize ( BOXA *boxa, l_float32 fract ); LEPT_DLL extern BOX * boxaGetMedian ( BOXA *boxa ); LEPT_DLL extern l_int32 boxaGetAverageSize ( BOXA *boxa, l_float32 *pw, l_float32 *ph ); @@ -325,12 +328,17 @@ LEPT_DLL extern BOXA * boxaPermuteRandom ( BOXA *boxad, BOXA *boxas ); LEPT_DLL extern l_int32 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 BOXA * boxaSmoothSequence ( BOXA *boxas, l_float32 factor, l_int32 subflag, l_int32 maxdiff, l_int32 debug ); +LEPT_DLL extern PTA * boxConvertToPta ( BOX *box, l_int32 ncorners ); +LEPT_DLL extern BOX * ptaConvertToBox ( PTA *pta ); +LEPT_DLL extern BOXA * boxaSmoothSequenceLS ( BOXA *boxas, l_float32 factor, l_int32 subflag, l_int32 maxdiff, l_int32 debug ); +LEPT_DLL extern BOXA * boxaSmoothSequenceMedian ( BOXA *boxas, l_int32 halfwin, l_int32 subflag, l_int32 maxdiff, 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 ); 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 ); LEPT_DLL extern l_int32 boxaPlotSides ( BOXA *boxa, const char *plotname, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, l_int32 outformat ); +LEPT_DLL extern BOXA * boxaFillSequence ( BOXA *boxas, l_int32 useflag, l_int32 debug ); LEPT_DLL extern l_int32 boxaGetExtent ( BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox ); LEPT_DLL extern l_int32 boxaGetCoverage ( BOXA *boxa, l_int32 wc, l_int32 hc, l_int32 exactflag, l_float32 *pfract ); LEPT_DLL extern l_int32 boxaaSizeRange ( BOXAA *baa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); @@ -398,6 +406,7 @@ LEPT_DLL extern l_int32 numaaCompareImagesByBoxes ( NUMAA *naa1, NUMAA *naa2, l_ LEPT_DLL extern l_int32 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 * 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_int32 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_int32 pixNumSignificantGrayColors ( PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_float32 minfract, l_int32 factor, l_int32 *pncolors ); LEPT_DLL extern l_int32 pixColorsForQuantization ( PIX *pixs, l_int32 thresh, l_int32 *pncolors, l_int32 *piscolor, l_int32 debug ); @@ -410,6 +419,7 @@ LEPT_DLL extern l_int32 getRGBFromIndex ( l_uint32 index, l_int32 sigbits, l_int LEPT_DLL extern l_int32 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_int32 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 ); @@ -439,9 +449,11 @@ LEPT_DLL extern l_int32 pixcmapGetColor32 ( PIXCMAP *cmap, l_int32 index, l_uint LEPT_DLL extern l_int32 pixcmapGetRGBA ( PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval ); LEPT_DLL extern l_int32 pixcmapGetRGBA32 ( PIXCMAP *cmap, l_int32 index, l_uint32 *pval32 ); LEPT_DLL extern l_int32 pixcmapResetColor ( PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_int32 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_int32 pixcmapHasColor ( PIXCMAP *cmap, l_int32 *pcolor ); LEPT_DLL extern l_int32 pixcmapIsOpaque ( PIXCMAP *cmap, l_int32 *popaque ); +LEPT_DLL extern l_int32 pixcmapIsBlackAndWhite ( PIXCMAP *cmap, l_int32 *pblackwhite ); LEPT_DLL extern l_int32 pixcmapCountGrayColors ( PIXCMAP *cmap, l_int32 *pngray ); LEPT_DLL extern l_int32 pixcmapGetRankIntensity ( PIXCMAP *cmap, l_float32 rankval, l_int32 *pindex ); LEPT_DLL extern l_int32 pixcmapGetNearestIndex ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); @@ -450,7 +462,9 @@ LEPT_DLL extern l_int32 pixcmapGetComponentRange ( PIXCMAP *cmap, l_int32 color, LEPT_DLL extern l_int32 pixcmapGetExtremeValue ( PIXCMAP *cmap, l_int32 type, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); 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 * pixcmapRead ( const char *filename ); LEPT_DLL extern PIXCMAP * pixcmapReadStream ( FILE *fp ); +LEPT_DLL extern l_int32 pixcmapWrite ( const char *filename, PIXCMAP *cmap ); LEPT_DLL extern l_int32 pixcmapWriteStream ( FILE *fp, PIXCMAP *cmap ); LEPT_DLL extern l_int32 pixcmapToArrays ( PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap ); LEPT_DLL extern l_int32 pixcmapToRGBTable ( PIXCMAP *cmap, l_uint32 **ptab, l_int32 *pncolors ); @@ -514,6 +528,18 @@ LEPT_DLL extern l_int32 convertRGBToYUV ( l_int32 rval, l_int32 gval, l_int32 bv LEPT_DLL extern l_int32 convertYUVToRGB ( l_int32 yval, l_int32 uval, l_int32 vval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); LEPT_DLL extern l_int32 pixcmapConvertRGBToYUV ( PIXCMAP *cmap ); LEPT_DLL extern l_int32 pixcmapConvertYUVToRGB ( PIXCMAP *cmap ); +LEPT_DLL extern FPIXA * pixConvertRGBToXYZ ( PIX *pixs ); +LEPT_DLL extern PIX * fpixaConvertXYZToRGB ( FPIXA *fpixa ); +LEPT_DLL extern l_int32 convertRGBToXYZ ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pfxval, l_float32 *pfyval, l_float32 *pfzval ); +LEPT_DLL extern l_int32 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_int32 convertXYZToLAB ( l_float32 xval, l_float32 yval, l_float32 zval, l_float32 *plval, l_float32 *paval, l_float32 *pbval ); +LEPT_DLL extern l_int32 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_int32 convertRGBToLAB ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pflval, l_float32 *pfaval, l_float32 *pfbval ); +LEPT_DLL extern l_int32 convertLABToRGB ( l_float32 flval, l_float32 faval, l_float32 fbval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); LEPT_DLL extern l_int32 pixEqual ( PIX *pix1, PIX *pix2, l_int32 *psame ); LEPT_DLL extern l_int32 pixEqualWithAlpha ( PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame ); LEPT_DLL extern l_int32 pixEqualWithCmap ( PIX *pix1, PIX *pix2, l_int32 *psame ); @@ -570,9 +596,6 @@ LEPT_DLL extern PIX * pixConvolveWithBias ( PIX *pixs, L_KERNEL *kel1, L_KERNEL 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 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 ); -LEPT_DLL extern void blockconvAccumLow ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 d, l_int32 wpls ); -LEPT_DLL extern 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 ); LEPT_DLL extern l_int32 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_int32 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 ); @@ -605,9 +628,12 @@ LEPT_DLL extern PTAA * dewarpRemoveShortLines ( PIX *pixs, PTAA *ptaas, l_float3 LEPT_DLL extern l_int32 dewarpBuildLineModel ( L_DEWARP *dew, l_int32 opensize, const char *debugfile ); LEPT_DLL extern l_int32 dewarpaModelStatus ( L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess ); LEPT_DLL extern l_int32 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_int32 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_int32 dewarpMinimize ( L_DEWARP *dew ); LEPT_DLL extern l_int32 dewarpPopulateFullRes ( L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y ); LEPT_DLL extern l_int32 dewarpSinglePage ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 use_both, PIX **ppixd, L_DEWARPA **pdewa, l_int32 debug ); +LEPT_DLL extern l_int32 dewarpSinglePageInit ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 use_both, PIX **ppixb, L_DEWARPA **pdewa ); +LEPT_DLL extern l_int32 dewarpSinglePageRun ( PIX *pixs, PIX *pixb, L_DEWARPA *dewa, PIX **ppixd, l_int32 debug ); LEPT_DLL extern l_int32 dewarpaListPages ( L_DEWARPA *dewa ); LEPT_DLL extern l_int32 dewarpaSetValidModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); LEPT_DLL extern l_int32 dewarpaInsertRefModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); @@ -674,6 +700,11 @@ LEPT_DLL extern l_int32 pixMeasureEdgeSmoothness ( PIX *pixs, l_int32 side, l_in LEPT_DLL extern NUMA * pixGetEdgeProfile ( PIX *pixs, l_int32 side, const char *debugfile ); LEPT_DLL extern l_int32 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 ( 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 ( l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern l_uint8 * decodeAscii85 ( char *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern char * reformatPacked64 ( 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 ); @@ -701,8 +732,8 @@ LEPT_DLL extern PIX * pixHalfEdgeByBandpass ( PIX *pixs, l_int32 sm1h, l_int32 s LEPT_DLL extern l_int32 fhmtautogen ( SELA *sela, l_int32 fileindex, const char *filename ); LEPT_DLL extern l_int32 fhmtautogen1 ( SELA *sela, l_int32 fileindex, const char *filename ); LEPT_DLL extern l_int32 fhmtautogen2 ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern PIX * pixHMTDwa_1 ( PIX *pixd, PIX *pixs, char *selname ); -LEPT_DLL extern PIX * pixFHMTGen_1 ( PIX *pixd, PIX *pixs, char *selname ); +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_int32 pixItalicWords ( PIX *pixs, BOXA *boxaw, PIX *pixw, BOXA **pboxa, l_int32 debugflag ); LEPT_DLL extern l_int32 pixOrientDetect ( PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug ); @@ -748,6 +779,7 @@ LEPT_DLL extern l_int32 fpixaGetCount ( FPIXA *fpixa ); LEPT_DLL extern l_int32 fpixaChangeRefcount ( FPIXA *fpixa, l_int32 delta ); LEPT_DLL extern FPIX * fpixaGetFPix ( FPIXA *fpixa, l_int32 index, l_int32 accesstype ); LEPT_DLL extern l_int32 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_int32 fpixaGetPixel ( FPIXA *fpixa, l_int32 index, l_int32 x, l_int32 y, l_float32 *pval ); LEPT_DLL extern l_int32 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 ); @@ -889,8 +921,6 @@ 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 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 ); -LEPT_DLL extern 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 ); LEPT_DLL extern PIX * pixDitherToBinary ( PIX *pixs ); LEPT_DLL extern PIX * pixDitherToBinarySpec ( PIX *pixs, l_int32 lowerclip, l_int32 upperclip ); LEPT_DLL extern PIX * pixThresholdToBinary ( PIX *pixs, l_int32 thresh ); @@ -964,14 +994,14 @@ LEPT_DLL extern l_int32 readHeaderJp2k ( const char *filename, l_int32 *pw, l_in LEPT_DLL extern l_int32 freadHeaderJp2k ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); LEPT_DLL extern l_int32 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 ); -LEPT_DLL extern PIX * pixReadStreamJp2k ( FILE *fp, l_uint32 reduction, BOX *box, l_int32 hint ); -LEPT_DLL extern l_int32 pixWriteJp2k ( const char *filename, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint ); -LEPT_DLL extern l_int32 pixWriteStreamJp2k ( FILE *fp, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint ); -LEPT_DLL extern PIX * pixReadMemJp2k ( const l_uint8 *data, size_t size, l_uint32 reduction, BOX *box, l_int32 hint ); -LEPT_DLL extern l_int32 pixWriteMemJp2k ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint ); -LEPT_DLL extern PIX * pixReadJpeg ( const char *filename, l_int32 cmflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); -LEPT_DLL extern PIX * pixReadStreamJpeg ( FILE *fp, l_int32 cmflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); +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_int32 pixWriteJp2k ( const char *filename, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); +LEPT_DLL extern l_int32 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_int32 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_int32 readHeaderJpeg ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); LEPT_DLL extern l_int32 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 ); @@ -1211,6 +1241,7 @@ LEPT_DLL extern l_int32 numaWindowedStats ( NUMA *nas, l_int32 wc, NUMA **pnam, LEPT_DLL extern NUMA * numaWindowedMean ( NUMA *nas, l_int32 wc ); LEPT_DLL extern NUMA * numaWindowedMeanSquare ( NUMA *nas, l_int32 wc ); LEPT_DLL extern l_int32 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 ); @@ -1245,6 +1276,7 @@ LEPT_DLL extern BOXA * pixSplitComponentWithProfile ( PIX *pixs, l_int32 delta, LEPT_DLL extern l_int32 pixSetSelectCmap ( PIX *pixs, BOX *box, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval ); LEPT_DLL extern l_int32 pixColorGrayRegionsCmap ( PIX *pixs, BOXA *boxa, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); LEPT_DLL extern l_int32 pixColorGrayCmap ( PIX *pixs, BOX *box, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_int32 pixColorGrayMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); LEPT_DLL extern l_int32 addColorizedGrayToCmap ( PIXCMAP *cmap, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval, NUMA **pna ); LEPT_DLL extern l_int32 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_int32 pixSetMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); @@ -1282,7 +1314,7 @@ LEPT_DLL extern l_int32 saConcatenatePdfToData ( SARRAY *sa, l_uint8 **pdata, si LEPT_DLL extern l_int32 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_int32 ptraConcatenatePdfToData ( L_PTRA *pa_data, SARRAY *sa, l_uint8 **pdata, size_t *pnbytes ); LEPT_DLL extern l_int32 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 ); +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_int32 l_generateCIData ( const char *fname, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); LEPT_DLL extern l_int32 pixGenerateCIData ( PIX *pixs, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); @@ -1410,9 +1442,11 @@ LEPT_DLL extern l_int32 pixSetMaskedGeneral ( PIX *pixd, PIX *pixm, l_uint32 val LEPT_DLL extern l_int32 pixCombineMasked ( PIX *pixd, PIX *pixs, PIX *pixm ); LEPT_DLL extern l_int32 pixCombineMaskedGeneral ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 x, l_int32 y ); LEPT_DLL extern l_int32 pixPaintThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val ); -LEPT_DLL extern l_int32 pixPaintSelfThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_int32 tilesize, l_int32 searchdir ); +LEPT_DLL extern l_int32 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 * pixMakeMaskFromLUT ( PIX *pixs, l_int32 *tab ); 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_int32 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 ); @@ -1443,6 +1477,7 @@ LEPT_DLL extern l_int32 pixAbsDiffInRect ( PIX *pix, BOX *box, l_int32 dir, l_fl LEPT_DLL extern l_int32 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_int32 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 ); @@ -1497,6 +1532,7 @@ LEPT_DLL extern PIX * pixClipMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y LEPT_DLL extern l_int32 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 * pixMakeFrameMask ( l_int32 w, l_int32 h, l_float32 hf1, l_float32 hf2, l_float32 vf1, l_float32 vf2 ); LEPT_DLL extern l_int32 pixClipToForeground ( PIX *pixs, PIX **ppixd, BOX **pbox ); LEPT_DLL extern l_int32 pixTestClipToForeground ( PIX *pixs, l_int32 *pcanclip ); LEPT_DLL extern l_int32 pixClipBoxToForeground ( PIX *pixs, BOX *boxs, PIX **ppixd, BOX **pboxd ); @@ -1629,8 +1665,8 @@ LEPT_DLL extern PIXA * pixaConvertTo1 ( PIXA *pixas, l_int32 thresh ); LEPT_DLL extern PIXA * pixaConvertTo8 ( PIXA *pixas, l_int32 cmapflag ); LEPT_DLL extern PIXA * pixaConvertTo8Color ( PIXA *pixas, l_int32 dither ); LEPT_DLL extern PIXA * pixaConvertTo32 ( PIXA *pixas ); -LEPT_DLL extern l_int32 convertToNUpFiles ( const char *dir, const char *substr, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, const char *fontdir, 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, const char *fontdir ); +LEPT_DLL extern l_int32 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 l_int32 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 ); @@ -1756,6 +1792,7 @@ LEPT_DLL extern l_int32 freadHeaderPng ( FILE *fp, l_int32 *pw, l_int32 *ph, l_i LEPT_DLL extern l_int32 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_int32 isPngInterlaced ( const char *filename, l_int32 *pinterlaced ); +LEPT_DLL extern l_int32 fgetPngColormapInfo ( FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency ); LEPT_DLL extern l_int32 pixWritePng ( const char *filename, PIX *pix, l_float32 gamma ); LEPT_DLL extern l_int32 pixWriteStreamPng ( FILE *fp, PIX *pix, l_float32 gamma ); LEPT_DLL extern l_int32 pixSetZlibCompression ( PIX *pix, l_int32 compval ); @@ -1814,8 +1851,6 @@ LEPT_DLL extern char * generateFlatePS ( const char *filein, L_COMP_DATA *cid, l LEPT_DLL extern l_int32 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 char * encodeAscii85 ( l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); -LEPT_DLL extern l_uint8 * decodeAscii85 ( char *ina, l_int32 insize, l_int32 *poutsize ); 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 ); @@ -1958,7 +1993,7 @@ LEPT_DLL extern PIX * pixReadMem ( const l_uint8 *data, size_t size ); LEPT_DLL extern l_int32 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_int32 ioFormatTest ( const char *filename ); LEPT_DLL extern L_RECOGA * recogaCreateFromRecog ( L_RECOG *recog ); -LEPT_DLL extern L_RECOGA * recogaCreateFromPixaa ( PIXAA *paa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift, const char *fontdir ); +LEPT_DLL extern L_RECOGA * recogaCreateFromPixaa ( PIXAA *paa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift ); LEPT_DLL extern L_RECOGA * recogaCreate ( l_int32 n ); LEPT_DLL extern void recogaDestroy ( L_RECOGA **precoga ); LEPT_DLL extern l_int32 recogaAddRecog ( L_RECOGA *recoga, L_RECOG *recog ); @@ -1969,9 +2004,9 @@ LEPT_DLL extern l_int32 recogGetCount ( L_RECOG *recog ); LEPT_DLL extern l_int32 recogGetIndex ( L_RECOG *recog, l_int32 *pindex ); LEPT_DLL extern L_RECOGA * recogGetParent ( L_RECOG *recog ); LEPT_DLL extern l_int32 recogSetBootflag ( L_RECOG *recog ); -LEPT_DLL extern L_RECOG * recogCreateFromRecog ( L_RECOG *recs, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift, const char *fontdir ); -LEPT_DLL extern L_RECOG * recogCreateFromPixa ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift, const char *fontdir ); -LEPT_DLL extern L_RECOG * recogCreate ( l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift, const char *fontdir ); +LEPT_DLL extern L_RECOG * recogCreateFromRecog ( L_RECOG *recs, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern L_RECOG * recogCreateFromPixa ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern L_RECOG * recogCreate ( l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, l_int32 maxyshift ); LEPT_DLL extern void recogDestroy ( L_RECOG **precog ); LEPT_DLL extern l_int32 recogAppend ( L_RECOG *recog1, L_RECOG *recog2 ); LEPT_DLL extern l_int32 recogGetClassIndex ( L_RECOG *recog, l_int32 val, char *text, l_int32 *pindex ); @@ -2036,7 +2071,7 @@ LEPT_DLL extern l_int32 recogDebugAverages ( L_RECOG *recog, l_int32 debug ); LEPT_DLL extern l_int32 recogShowAverageTemplates ( L_RECOG *recog ); LEPT_DLL extern l_int32 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_int32 recogMakeBmf ( L_RECOG *recog, const char *fontdir, l_int32 size ); +LEPT_DLL extern l_int32 recogResetBmf ( L_RECOG *recog, l_int32 size ); LEPT_DLL extern l_int32 regTestSetup ( l_int32 argc, char **argv, L_REGPARAMS **prp ); LEPT_DLL extern l_int32 regTestCleanup ( L_REGPARAMS *rp ); LEPT_DLL extern l_int32 regTestCompareValues ( L_REGPARAMS *rp, l_float32 val1, l_float32 val2, l_float32 delta ); @@ -2323,6 +2358,10 @@ LEPT_DLL extern l_int32 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_int32 lstackPrint ( FILE *fp, L_STACK *lstack ); +LEPT_DLL extern L_STRCODE * strcodeCreate ( l_int32 fileno ); +LEPT_DLL extern l_int32 strcodeCreateFromFile ( const char *filein, l_int32 fileno, const char *outdir ); +LEPT_DLL extern l_int32 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 * sudokuReadFile ( const char *filename ); LEPT_DLL extern l_int32 * sudokuReadString ( const char *str ); LEPT_DLL extern L_SUDOKU * sudokuCreate ( l_int32 *array ); @@ -2371,6 +2410,7 @@ 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_int32 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_int32 stringSplitOnToken ( char *cstr, const char *seps, char **phead, char **ptail ); @@ -2433,6 +2473,8 @@ 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 l_int32 pixHtmlViewer ( const char *dirin, const char *dirout, const char *rootname, l_int32 thumbwidth, l_int32 viewwidth, l_int32 copyorig ); LEPT_DLL extern PIX * pixSimpleCaptcha ( PIX *pixs, l_int32 border, l_int32 nterms, l_uint32 seed, l_uint32 color, l_int32 cmapflag ); @@ -2460,11 +2502,13 @@ LEPT_DLL extern l_int32 pixWriteStreamWebP ( FILE *fp, PIX *pixs, l_int32 qualit LEPT_DLL extern l_int32 pixWriteMemWebP ( l_uint8 **pencdata, size_t *pencsize, PIX *pixs, l_int32 quality, l_int32 lossless ); LEPT_DLL extern l_int32 pixaWriteFiles ( const char *rootname, PIXA *pixa, l_int32 format ); LEPT_DLL extern l_int32 pixWrite ( const char *filename, PIX *pix, l_int32 format ); +LEPT_DLL extern l_int32 pixWriteAutoFormat ( const char *filename, PIX *pix ); LEPT_DLL extern l_int32 pixWriteStream ( FILE *fp, PIX *pix, l_int32 format ); LEPT_DLL extern l_int32 pixWriteImpliedFormat ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive ); LEPT_DLL extern l_int32 pixWriteTempfile ( const char *dir, const char *tail, PIX *pix, l_int32 format, char **pfilename ); LEPT_DLL extern l_int32 pixChooseOutputFormat ( PIX *pix ); LEPT_DLL extern l_int32 getImpliedFileFormat ( const char *filename ); +LEPT_DLL extern l_int32 pixGetAutoFormat ( PIX *pix, l_int32 *pformat ); LEPT_DLL extern const char * getFormatExtension ( l_int32 format ); LEPT_DLL extern l_int32 pixWriteMem ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 format ); LEPT_DLL extern l_int32 pixDisplay ( PIX *pixs, l_int32 x, l_int32 y ); diff --git a/liblept/include/alltypes.h b/liblept/include/alltypes.h index b190065..7977a5e 100644 --- a/liblept/include/alltypes.h +++ b/liblept/include/alltypes.h @@ -10,7 +10,7 @@ - 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 @@ -56,6 +56,7 @@ #include "pix.h" #include "recog.h" #include "regutils.h" +#include "stringcode.h" #include "sudoku.h" #include "watershed.h" diff --git a/liblept/include/array.h b/liblept/include/array.h index f33bd4a..8fdcbb3 100644 --- a/liblept/include/array.h +++ b/liblept/include/array.h @@ -10,7 +10,7 @@ - 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 @@ -45,7 +45,7 @@ */ -/*------------------------------------------------------------------------* +/*------------------------------------------------------------------------* * Array Structs * *------------------------------------------------------------------------*/ @@ -144,7 +144,7 @@ struct L_Bytea typedef struct L_Bytea L_BYTEA; -/*------------------------------------------------------------------------* +/*------------------------------------------------------------------------* * Array flags * *------------------------------------------------------------------------*/ /* Flags for interpolation in Numa */ diff --git a/liblept/include/bbuffer.h b/liblept/include/bbuffer.h index 3d308eb..204cfdd 100644 --- a/liblept/include/bbuffer.h +++ b/liblept/include/bbuffer.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/bmf.h b/liblept/include/bmf.h index 007534c..25d40f2 100644 --- a/liblept/include/bmf.h +++ b/liblept/include/bmf.h @@ -10,7 +10,7 @@ - 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 @@ -27,7 +27,7 @@ #ifndef LEPTONICA_BMF_H #define LEPTONICA_BMF_H -/* +/* * bmf.h * * Simple data structure to hold bitmap fonts and related data diff --git a/liblept/include/bmfdata.h b/liblept/include/bmfdata.h new file mode 100644 index 0000000..6c46e6d --- /dev/null +++ b/liblept/include/bmfdata.h @@ -0,0 +1,634 @@ +/*====================================================================* + - 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. + *====================================================================*/ + +/* + * 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 iamge 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" + "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" + "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" + "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" + "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" + "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" + "/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" + "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" + "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/liblept/include/bmp.h b/liblept/include/bmp.h index c50b7b4..212dce5 100644 --- a/liblept/include/bmp.h +++ b/liblept/include/bmp.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/ccbord.h b/liblept/include/ccbord.h index 9c976aa..47ec57a 100644 --- a/liblept/include/ccbord.h +++ b/liblept/include/ccbord.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/environ.h b/liblept/include/environ.h index 0980561..a88a60d 100644 --- a/liblept/include/environ.h +++ b/liblept/include/environ.h @@ -82,7 +82,6 @@ typedef unsigned int uintptr_t; typedef intptr_t l_intptr_t; typedef uintptr_t l_uintptr_t; -typedef void *L_TIMER; /*--------------------------------------------------------------------* @@ -97,29 +96,35 @@ typedef void *L_TIMER; * I/O libraries, plus zlib. Setting any of these to 0 here causes * non-functioning stubs to be linked. */ -#ifndef HAVE_CONFIG_H +#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) #define HAVE_LIBJPEG 1 #define HAVE_LIBTIFF 1 #define HAVE_LIBPNG 1 #define HAVE_LIBZ 1 -#define HAVE_LIBGIF 1 +#define HAVE_LIBGIF 0 #define HAVE_LIBUNGIF 0 #define HAVE_LIBWEBP 0 #define HAVE_LIBJP2K 0 -#endif /* ~HAVE_CONFIG_H */ + + /* Leptonica supports both OpenJPEG 2.0 and 2.1. If you have a + * version of openjpeg (HAVE_LIBJP2K) that is not 2.1, set the + * path to the openjpeg.h header in angle brackets here. */ +#define LIBJP2K_HEADER +#endif /* ! HAVE_CONFIG_H etc. */ /* * 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, 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 be 1 here. + * 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. */ -#ifndef HAVE_CONFIG_H -#define HAVE_FMEMOPEN 0 -#endif /* ~HAVE_CONFIG_H */ +#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) && !defined(_MSC_VER) +#define HAVE_FMEMOPEN 1 +#endif /* ! HAVE_CONFIG_H etc. */ /*--------------------------------------------------------------------* @@ -142,6 +147,33 @@ typedef void *L_TIMER; #define USE_PSIO 1 +/*--------------------------------------------------------------------* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * USER CONFIGURABLE * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * Optional subdirectory translation for read/write to /tmp * + *--------------------------------------------------------------------*/ +/* + * 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. + * Consequently, all temp files on Windows are written to /leptonica/ + * or subdirectories of it, with the translation: + * /tmp/xxx --> /leptonica/xxx + * + * This is not the case for Unix, but we provide an option for reading + * and writing on Unix with this translation: + * /tmp/xxx --> /tmp/leptonica/xxx + * By default, leptonica is distributed for Unix without this translation + * (except on Cygwin, which runs on Windows). + */ +#if defined (__CYGWIN__) + #define ADD_LEPTONICA_SUBDIR 1 +#else + #define ADD_LEPTONICA_SUBDIR 0 +#endif + + /*--------------------------------------------------------------------* * Built-in types * *--------------------------------------------------------------------*/ @@ -230,6 +262,19 @@ enum { }; +/*------------------------------------------------------------------------* + * Timing structs * + *------------------------------------------------------------------------*/ +typedef void *L_TIMER; +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 * * * diff --git a/liblept/include/jbclass.h b/liblept/include/jbclass.h index 0a22ab3..0c28c86 100644 --- a/liblept/include/jbclass.h +++ b/liblept/include/jbclass.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/morph.h b/liblept/include/morph.h index ca2065a..bf02740 100644 --- a/liblept/include/morph.h +++ b/liblept/include/morph.h @@ -126,7 +126,7 @@ enum { /*-------------------------------------------------------------------------* * Direction flags for grayscale morphology, granulometry, * - * composable Sels, and convolution * + * composable Sels, convolution, etc. * *-------------------------------------------------------------------------*/ enum { L_HORIZ = 1, diff --git a/liblept/include/pix.h b/liblept/include/pix.h index 0eb4d3c..e8351b6 100644 --- a/liblept/include/pix.h +++ b/liblept/include/pix.h @@ -94,6 +94,7 @@ * Box size adjustment flags * Flags for selecting box boundaries from two choices * Handling overlapping bounding boxes in boxa + * Flags for replacing invalid boxes * Horizontal warp * Pixel selection for resampling * Thinning flags @@ -940,7 +941,9 @@ enum { enum { L_USE_MINSIZE = 1, /* use boundaries giving min size */ L_USE_MAXSIZE = 2, /* use boundaries giving max size */ - L_SUB_ON_BIG_DIFF = 3 /* substitute boundary if big abs diff */ + L_SUB_ON_BIG_DIFF = 3, /* substitute boundary if big abs diff */ + L_USE_CAPPED_MIN = 4, /* substitute boundary with capped min */ + L_USE_CAPPED_MAX = 5 /* substitute boundary with capped max */ }; /*-------------------------------------------------------------------------* @@ -951,6 +954,14 @@ enum { L_REMOVE_SMALL = 2 /* only remove smaller */ }; +/*-------------------------------------------------------------------------* + * Flags for replacing invalid boxes * + *-------------------------------------------------------------------------*/ +enum { + L_USE_ALL_BOXES = 1, /* consider all boxes in the sequence */ + L_USE_SAME_PARITY_BOXES = 2 /* consider boxes with the same parity */ +}; + /*-------------------------------------------------------------------------* * Horizontal warp * *-------------------------------------------------------------------------*/ diff --git a/liblept/include/readbarcode.h b/liblept/include/readbarcode.h index 44ae29c..4cfb9d5 100644 --- a/liblept/include/readbarcode.h +++ b/liblept/include/readbarcode.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/recog.h b/liblept/include/recog.h index 6a449d2..72a4e5d 100644 --- a/liblept/include/recog.h +++ b/liblept/include/recog.h @@ -153,12 +153,11 @@ struct L_Recog { struct Pix *pixdb_range; /* debug: best matches within range */ struct Pixa *pixadb_boot; /* debug: bootstrap training results */ struct Pixa *pixadb_split; /* debug: splitting results */ - char *fontdir; /* directory for bitmapped fonts */ 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 */ + 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 */ l_int32 bootrecog; /* 1 if using bootstrap samples; else 0 */ l_int32 index; /* recog index in recoga; -1 if no parent */ struct L_Recoga *parent; /* ptr to parent array; can be null */ diff --git a/liblept/include/stringcode.h b/liblept/include/stringcode.h new file mode 100644 index 0000000..6c8d9d1 --- /dev/null +++ b/liblept/include/stringcode.h @@ -0,0 +1,48 @@ +/*====================================================================* + - 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 + +/* + * stringcode.h + * + * Data structure to hold accumulating generated code for storing + * and extracing serializable leptonica objects (e.g., pixa, recog). + */ + +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; + +#endif /* LEPTONICA_STRINGCODE_H */ diff --git a/liblept/include/sudoku.h b/liblept/include/sudoku.h index cd9f80e..8cacf9d 100644 --- a/liblept/include/sudoku.h +++ b/liblept/include/sudoku.h @@ -10,7 +10,7 @@ - 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 diff --git a/liblept/include/watershed.h b/liblept/include/watershed.h index 5350479..91ca355 100644 --- a/liblept/include/watershed.h +++ b/liblept/include/watershed.h @@ -10,7 +10,7 @@ - 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 @@ -27,14 +27,14 @@ #ifndef LEPTONICA_WATERSHED_H #define LEPTONICA_WATERSHED_H -/* +/* * watershed.h * * Simple data structure to hold watershed data. * All data here is owned by the L_WShed and must be freed. */ -struct L_WShed +struct L_WShed { struct Pix *pixs; /* clone of input 8 bpp pixs */ struct Pix *pixm; /* clone of input 1 bpp seed (marker) pixm */ diff --git a/liblept/leptonica.vcxproj b/liblept/leptonica.vcxproj index 9e46d79..12ccec8 100644 --- a/liblept/leptonica.vcxproj +++ b/liblept/leptonica.vcxproj @@ -1,5 +1,5 @@  - + DLL_Debug @@ -35,46 +35,47 @@ - liblept-171 + liblept-172 {1D7AB886-BECA-4A97-89CE-7381D3F5DE00} liblept Win32Proj + 8.1 DynamicLibrary - v120 + v140_xp StaticLibrary - v120 + v140_xp MultiByte DynamicLibrary - v120 + v140_xp StaticLibrary - v120 + v140_xp MultiByte DynamicLibrary - v120 + v140_xp StaticLibrary - v120 + v140_xp MultiByte DynamicLibrary - v120 + v140_xp StaticLibrary - v120 + v140_xp MultiByte @@ -170,7 +171,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp zlib$(ZLIB_VERSION)-static-mtdll.lib;libpng$(LIBPNG_VERSION)-static-mtdll.lib;libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib;libtiff$(LIBTIFF_VERSION)-static-mtdll.lib;giflib$(GIFLIB_VERSION)-static-mtdll.lib;%(AdditionalDependencies) @@ -205,7 +206,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp zlib$(ZLIB_VERSION)-static-mtdll.lib;libpng$(LIBPNG_VERSION)-static-mtdll.lib;libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib;libtiff$(LIBTIFF_VERSION)-static-mtdll.lib;giflib$(GIFLIB_VERSION)-static-mtdll.lib;%(AdditionalDependencies) @@ -241,7 +242,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp zlib$(ZLIB_VERSION)-static-mtdll.lib;libpng$(LIBPNG_VERSION)-static-mtdll.lib;libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib;libtiff$(LIBTIFF_VERSION)-static-mtdll.lib;giflib$(GIFLIB_VERSION)-static-mtdll.lib;%(AdditionalDependencies) @@ -276,7 +277,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp zlib$(ZLIB_VERSION)-static-mtdll.lib;libpng$(LIBPNG_VERSION)-static-mtdll.lib;libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib;libtiff$(LIBTIFF_VERSION)-static-mtdll.lib;giflib$(GIFLIB_VERSION)-static-mtdll.lib;%(AdditionalDependencies) @@ -310,7 +311,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp false @@ -347,7 +348,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp false @@ -384,7 +385,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp false @@ -420,7 +421,7 @@ Level3 ProgramDatabase - Default + CompileAsCpp false @@ -454,6 +455,7 @@ + @@ -485,6 +487,7 @@ + @@ -591,6 +594,7 @@ + @@ -614,6 +618,7 @@ + @@ -633,6 +638,7 @@ + diff --git a/liblept/leptonica.vcxproj.filters b/liblept/leptonica.vcxproj.filters index f6c43da..78018ca 100644 --- a/liblept/leptonica.vcxproj.filters +++ b/liblept/leptonica.vcxproj.filters @@ -512,6 +512,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -598,5 +607,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/liblept/src/adaptmap.c b/liblept/src/adaptmap.c index ffbcd39..eac6d34 100644 --- a/liblept/src/adaptmap.c +++ b/liblept/src/adaptmap.c @@ -35,6 +35,9 @@ * 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 @@ -152,6 +155,54 @@ static l_int32 *iaaGetLinearTRC(l_int32 **iaa, l_int32 diff); #endif /* ~NO_CONSOLE_IO */ +/*------------------------------------------------------------------* + * Clean background to white using background normalization * + *------------------------------------------------------------------*/ +/*! + * pixCleanBackgroundToWhite() + * + * Input: pixs (8 bpp grayscale or 32 bpp rgb) + * pixim ( 1 bpp 'image' mask; can be null) + * pixg ( 8 bpp grayscale version; can be null) + * gamma (gamma correction; must be > 0.0; typically ~1.0) + * blackval (dark value to set to black (0)) + * 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); + pixGammaTRC(pixd, pixd, gamma, blackval, whiteval); + return pixd; +} + + /*------------------------------------------------------------------* * Adaptive background normalization * *------------------------------------------------------------------*/ diff --git a/liblept/src/allheaders_top.txt b/liblept/src/allheaders_top.txt index 62654e3..aa0535f 100644 --- a/liblept/src/allheaders_top.txt +++ b/liblept/src/allheaders_top.txt @@ -29,7 +29,7 @@ #define LIBLEPT_MAJOR_VERSION 1 -#define LIBLEPT_MINOR_VERSION 71 +#define LIBLEPT_MINOR_VERSION 72 #include "alltypes.h" diff --git a/liblept/src/bbuffer.c b/liblept/src/bbuffer.c index 35430a7..4423fc6 100644 --- a/liblept/src/bbuffer.c +++ b/liblept/src/bbuffer.c @@ -41,10 +41,6 @@ * l_int32 bbufferWrite() * l_int32 bbufferWriteStream() * - * Accessors - * l_int32 bbufferBytesToWrite() - * - * * 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 @@ -472,29 +468,3 @@ l_int32 nleft, nout; return 0; } - -/*--------------------------------------------------------------------------* - * Accessors * - *--------------------------------------------------------------------------*/ -/*! - * bbufferBytesToWrite() - * - * Input: bbuffer - * &nbytes () - * Return: 0 if OK; 1 on error - */ -l_int32 -bbufferBytesToWrite(BBUFFER *bb, - size_t *pnbytes) -{ - PROCNAME("bbufferBytesToWrite"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - - *pnbytes = bb->n - bb->nwritten; - return 0; -} - diff --git a/liblept/src/bilateral.c b/liblept/src/bilateral.c index a81cfe1..b3b9a4c 100644 --- a/liblept/src/bilateral.c +++ b/liblept/src/bilateral.c @@ -391,7 +391,7 @@ PIXA *pixac; pixGetDimensions(pixs, &w, &h, NULL); wd = (w + reduction - 1) / reduction; hd = (h + reduction - 1) / reduction; - halfwidth = (l_int32)(2 * sstdev + 0.5); + halfwidth = (l_int32)(2.0 * sstdev); for (index = 0; index < ncomps; index++) { pixt = pixCopy(NULL, pixsc); datat = pixGetData(pixt); diff --git a/liblept/src/bmf.c b/liblept/src/bmf.c index ff919d8..5f10441 100644 --- a/liblept/src/bmf.c +++ b/liblept/src/bmf.c @@ -24,7 +24,6 @@ - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ - /* * bmf.c * @@ -39,6 +38,8 @@ * * PIXA *pixaGetFont() * l_int32 pixaSaveFont() + * PIXA *pixaGenerateFontFromFile() + * PIXA *pixaGenerateFontFromString() * PIXA *pixaGenerateFont() * static l_int32 pixGetTextBaseline() * static l_int32 bmfMakeAsciiTables() @@ -58,29 +59,15 @@ * * The set of ascii characters from 32 through 126 are the 95 * printable ascii chars. Palatino-Roman is missing char 92, '\'. - * I have substituted '/', char 47, for 92, so that there will be - * 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 '!'. + * 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" -#define NFONTS 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[NFONTS][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 l_float32 VERT_FRACT_SEP = 0.3; #ifndef NO_CONSOLE_IO @@ -99,46 +86,62 @@ static l_int32 bmfMakeAsciiTables(L_BMF *bmf); /*! * bmfCreate() * - * Input: dir (directory holding pixa of character set) - * size (4, 6, 8, ... , 20) + * Input: dir ( directory holding pixa of character set) + * fontsize (4, 6, 8, ... , 20) * Return: bmf (holding the bitmap font and associated information) * * Notes: - * (1) This first tries to read a pre-computed pixa file with the - * 95 ascii chars in it. If the file is not found, it - * creates the pixa from the raw image. It then generates all - * associated data required to use the bmf. + * (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 size) + 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); if ((bmf = (L_BMF *)CALLOC(1, sizeof(L_BMF))) == NULL) return (L_BMF *)ERROR_PTR("bmf not made", procName, NULL); - /* Look for the pixa */ - pixa = pixaGetFont(dir, size, &bmf->baseline1, &bmf->baseline2, - &bmf->baseline3); + if (!dir) { /* Generate from a string */ + L_INFO("Generating pixa of bitmap fonts from string\n", procName); + pixa = pixaGenerateFontFromString(fontsize, &bmf->baseline1, + &bmf->baseline2, &bmf->baseline3); + } else { /* Look for the pixa in a directory */ + L_INFO("Locating pixa of bitmap fonts in a file\n", procName); + 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 not found, make it */ if (!pixa) { - L_INFO("Generating pixa of bitmap fonts\n", procName); - pixa = pixaGenerateFont(dir, size, &bmf->baseline1, &bmf->baseline2, - &bmf->baseline3); - if (!pixa) { - bmfDestroy(&bmf); - return (L_BMF *)ERROR_PTR("font pixa not made", procName, NULL); - } + bmfDestroy(&bmf); + return (L_BMF *)ERROR_PTR("font pixa not made", procName, NULL); } bmf->pixa = pixa; - bmf->size = size; - bmf->directory = stringNew(dir); + bmf->size = fontsize; + if (dir) bmf->directory = stringNew(dir); bmfMakeAsciiTables(bmf); return bmf; } @@ -208,6 +211,7 @@ PIXA *pixa; if ((pixa = bmf->pixa) == NULL) return (PIX *)ERROR_PTR("pixa not found", procName, NULL); + return pixaGetPix(pixa, i, L_CLONE); } @@ -226,7 +230,6 @@ bmfGetWidth(L_BMF *bmf, l_int32 *pw) { l_int32 i, index; -PIX *pix; PIXA *pixa; PROCNAME("bmfGetWidth"); @@ -247,12 +250,8 @@ PIXA *pixa; if ((pixa = bmf->pixa) == NULL) return ERROR_INT("pixa not found", procName, 1); - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) - return ERROR_INT("pix not found", procName, 1); - *pw = pixGetWidth(pix); - pixDestroy(&pix); - return 0; + return pixaGetPixDimensions(pixa, i, pw, NULL, NULL); } @@ -299,7 +298,7 @@ l_int32 bl, index; * pixaGetFont() * * Input: dir (directory holding pixa of character set) - * size (4, 6, 8, ... , 20) + * fontsize (4, 6, 8, ... , 20) * &bl1 ( baseline of row 1) * &bl2 ( baseline of row 2) * &bl3 ( baseline of row 3) @@ -310,7 +309,7 @@ l_int32 bl, index; */ PIXA * pixaGetFont(const char *dir, - l_int32 size, + l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2) @@ -321,8 +320,8 @@ PIXA *pixa; PROCNAME("pixaGetFont"); - fileno = (size / 2) - 2; - if (fileno < 0 || fileno > NFONTS) + 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); @@ -343,21 +342,23 @@ PIXA *pixa; /*! * pixaSaveFont() * - * Input: indir (directory holding image of character set) + * Input: indir ( directory holding image of character set) * outdir (directory into which the output pixa file * will be written) - * size (in pts, at 300 ppi) + * fontsize (in pts, at 300 ppi) * Return: 0 if OK, 1 on error * * Notes: * (1) This saves a font of a particular size. - * (2) prog/genfonts calls this function for each of the + * (2) If @dir == 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_int32 pixaSaveFont(const char *indir, const char *outdir, - l_int32 size) + l_int32 fontsize) { char *pathname; l_int32 bl1, bl2, bl3; @@ -365,18 +366,26 @@ PIXA *pixa; PROCNAME("pixaSaveFont"); - if (size < 4 || size > 20 || (size % 2)) - return ERROR_INT("size must be in {4, 6, ..., 20}", procName, 1); + if (fontsize < 4 || fontsize > 20 || (fontsize % 2)) + return ERROR_INT("fontsize must be in {4, 6, ..., 20}", procName, 1); - if ((pixa = pixaGenerateFont(indir, size, &bl1, &bl2, &bl3)) == NULL) + if (!indir) { /* Generate from a string */ + L_INFO("Generating pixa of bitmap fonts from string\n", procName); + pixa = pixaGenerateFontFromString(fontsize, &bl1, &bl2, &bl3); + } else { /* Generate from an image file */ + L_INFO("Generating pixa of bitmap fonts from a file\n", procName); + pixa = pixaGenerateFontFromFile(indir, fontsize, &bl1, &bl2, &bl3); + } + if (!pixa) return ERROR_INT("pixa not made", procName, 1); - pathname = genPathname(outdir, outputfonts[(size - 4) / 2]); + + pathname = genPathname(outdir, outputfonts[(fontsize - 4) / 2]); pixaWrite(pathname, pixa); #if DEBUG_FONT_GEN - fprintf(stderr, "Found %d chars in font size %d\n", - pixaGetCount(pixa), size); - fprintf(stderr, "Baselines are at: %d, %d, %d\n", bl1, bl2, bl3); + 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 */ FREE(pathname); @@ -386,10 +395,10 @@ PIXA *pixa; /*! - * pixaGenerateFont() + * pixaGenerateFontFromFile() * * Input: dir (directory holding image of character set) - * size (4, 6, 8, ... , 20, in pts at 300 ppi) + * fontsize (4, 6, 8, ... , 20, in pts at 300 ppi) * &bl1 ( baseline of row 1) * &bl2 ( baseline of row 2) * &bl3 ( baseline of row 3) @@ -406,59 +415,189 @@ PIXA *pixa; * '\' character, so that we have representations of all 95 * printable chars. * - * Computation of the bitmaps and baselines for a single - * font takes from 40 to 200 msec on a 2 GHz processor, - * depending on the size. Use pixaGetFont() to read the - * generated character set directly from files that were - * produced in prog/genfonts.c using this function. + * 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. */ PIXA * -pixaGenerateFont(const char *dir, - l_int32 size, - l_int32 *pbl0, - l_int32 *pbl1, - l_int32 *pbl2) +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 = genPathname(dir, inputfonts[fileno]); + pix = pixRead(pathname); + FREE(pathname); + if (!pix) + return (PIXA *)ERROR_PTR("pix not all defined", procName, NULL); + + pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); + pixDestroy(&pix); + return pixa; +} + + +/*! + * pixaGenerateFontFromString() + * + * Input: fontsize (4, 6, 8, ... , 20, in pts at 300 ppi) + * &bl1 ( baseline of row 1) + * &bl2 ( baseline of row 2) + * &bl3 ( baseline of row 3) + * Return: pixa of font bitmaps for 95 characters, or null on error + * + * Notes: + * (1) See pixaGenerateFontFromFile() for details. + */ +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); + FREE(data); + if (!pix) + return (PIXA *)ERROR_PTR("pix not made", procName, NULL); + + pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); + pixDestroy(&pix); + return pixa; +} + + +/*! + * pixaGenerateFont() + * + * Input: pix (of 95 characters in 3 rows) + * fontsize (4, 6, 8, ... , 20, in pts at 300 ppi) + * &bl1 ( baseline of row 1) + * &bl2 ( baseline of row 2) + * &bl3 ( 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. + */ +PIXA * +pixaGenerateFont(PIX *pixs, + l_int32 fontsize, + l_int32 *pbl0, + l_int32 *pbl1, + l_int32 *pbl2) { -char *pathname; -l_int32 fileno; l_int32 i, j, nrows, nrowchars, nchars, h, yval; l_int32 width, height; l_int32 baseline[3]; -l_int32 *tab; +l_int32 *tab = NULL; BOX *box, *box1, *box2; BOXA *boxar, *boxac, *boxacs; -PIX *pixs, *pixt1, *pixt2, *pixt3; -PIX *pixr, *pixrc, *pixc; +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; - - fileno = (size / 2) - 2; - if (fileno < 0 || fileno > NFONTS) - return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); - tab = makePixelSumTab8(); - pathname = genPathname(dir, inputfonts[fileno]); - if ((pixs = pixRead(pathname)) == NULL) - return (PIXA *)ERROR_PTR("pixs not all defined", procName, NULL); - FREE(pathname); - - pixa = pixaCreate(95); - pixt1 = pixMorphSequence(pixs, "c1.35 + c101.1", 0); - boxar = pixConnComp(pixt1, NULL, 8); /* one box for each row */ - pixDestroy(&pixt1); + 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); + } + } + FREE(ia); + numaDestroy(&na); nrows = boxaGetCount(boxar); #if DEBUG_FONT_GEN - fprintf(stderr, "For font %s, number of rows is %d\n", - inputfonts[fileno], nrows); + L_INFO("For fontsize %s, have %d rows\n", procName, fontsize, nrows); #endif /* DEBUG_FONT_GEN */ if (nrows != 3) { - L_INFO("nrows = %d; skipping font %d\n", procName, nrows, fileno); + L_INFO("nrows = %d; skipping fontsize %d\n", procName, nrows, fontsize); 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 */ @@ -466,20 +605,18 @@ PIXA *pixa; baseline[i] = yval; #if DEBUG_BASELINE - { PIX *pixbl; - fprintf(stderr, "row %d, yval = %d, h = %d\n", - i, yval, pixGetHeight(pixr)); - pixbl = pixCopy(NULL, pixr); - pixRenderLine(pixbl, 0, yval, pixGetWidth(pixbl), yval, 1, + 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 ) - pixWrite("junktl0", pixbl, IFF_PNG); + pixWrite("/tmp/baseline/row0.png", pix1, IFF_PNG); else if (i == 1) - pixWrite("junktl1", pixbl, IFF_PNG); + pixWrite("/tmp/baseline/row1.png", pix1, IFF_PNG); else - pixWrite("junktl2", pixbl, IFF_PNG); - pixDestroy(&pixbl); - } + pixWrite("/tmp/baseline/row2.png", pix1, IFF_PNG); + pixDestroy(&pix1); #endif /* DEBUG_BASELINE */ boxDestroy(&box); @@ -517,6 +654,7 @@ PIXA *pixa; boxaDestroy(&boxac); boxaDestroy(&boxacs); } + FREE(tab); nchars = pixaGetCount(pixa); if (nchars != 95) @@ -528,31 +666,26 @@ PIXA *pixa; /* Fix the space character up; it should have no ON pixels, * and be about twice as wide as the '!' character. */ - pixt2 = pixaGetPix(pixa, 0, L_CLONE); - width = 2 * pixGetWidth(pixt2); - height = pixGetHeight(pixt2); - pixDestroy(&pixt2); - pixt2 = pixCreate(width, height, 1); - pixaReplacePix(pixa, 0, pixt2, NULL); + 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 */ - pixt2 = pixaGetPix(pixa, 15, L_CLONE); - pixt3 = pixFlipLR(NULL, pixt2); - pixDestroy(&pixt2); - pixaReplacePix(pixa, 60, pixt3, NULL); + pix1 = pixaGetPix(pixa, 15, L_CLONE); + pix2 = pixFlipLR(NULL, pix1); + pixDestroy(&pix1); + pixaReplacePix(pixa, 60, pix2, NULL); #if DEBUG_CHARS - { PIX *pixd; - pixd = pixaDisplayTiled(pixa, 1500, 0, 10); - pixDisplay(pixd, 100 * i, 200); - pixDestroy(&pixd); - } + pix1 = pixaDisplayTiled(pixa, 1500, 0, 10); + pixDisplay(pix1, 100 * i, 200); + pixDestroy(&pix1); #endif /* DEBUG_CHARS */ - pixDestroy(&pixs); boxaDestroy(&boxar); - FREE(tab); - return pixa; } diff --git a/liblept/src/bmpio.c b/liblept/src/bmpio.c index b3de7cc..e775dd2 100644 --- a/liblept/src/bmpio.c +++ b/liblept/src/bmpio.c @@ -451,7 +451,7 @@ RGBA_QUAD *pquad; fwrite(&bfSize, 1, 2, fp); fwrite(&bfFill1, 1, 2, fp); fwrite(&bfReserved1, 1, 2, fp); - fwrite(&bfReserved1, 1, 2, fp); + fwrite(&bfReserved2, 1, 2, fp); fwrite(&bfOffBits, 1, 2, fp); fwrite(&bfFill2, 1, 2, fp); @@ -585,7 +585,8 @@ PIX *pix; return (PIX *)ERROR_PTR("stream not opened", procName, NULL); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (PIX *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(cdata, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -631,7 +632,8 @@ FILE *fp; ret = pixWriteStreamBmp(fp, pix); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); ret = pixWriteStreamBmp(fp, pix); rewind(fp); *pdata = l_binaryReadStream(fp, psize); diff --git a/liblept/src/bootnumgen.c b/liblept/src/bootnumgen.c new file mode 100644 index 0000000..de47c0b --- /dev/null +++ b/liblept/src/bootnumgen.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. + *====================================================================*/ + +/* + * bootnumgen.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(103); // arbitrary integer + * strcodeGenerate(strc, "recog/digits/bootnum1.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 = (PIXA *)l_bootnum_gen(); + */ + +#include +#include "allheaders.h" + +/*---------------------------------------------------------------------*/ +/* Serialized string */ +/*---------------------------------------------------------------------*/ +static const char *l_bootnum = + "eJy1nAdQU9kb9m+4IQlw4YZ+qYkUwZ6ANCkJhGpF14I9gCJ27KhIEsBQREDWVbABll103V2x" + "YlsSAiIKAq66rKISsCDqbhDUoCH3S4Dw7XzXbyaXnT8zESbDyPPLOed93nPe9x79yJWJ0fS5" + "yzduWrl+Hd1Nf/qWtTHLN9LXx9ETVibS/elMNw99/aD1/5/fiVmfuHyT6rcY+upfX8hYPIme" + "uHHgnbH07YM/6WdGTg8z1LfWBwDAMCI8eJb6u+qFEFT/AHp7JveqvyWEz98EaL6Oxox0U7+5" + "OSRqM2f92rXL120GPJ4XRjir3mRHBAfOphTGykeIZQJCmn04w1YpTlCQM9YoxI2R4gS2jpM8" + "PVVKAyU8+irgBBgp3EVO9SODqUROqq5ETANMptvkW/xZbaf+QxEh04N/DeKm9Mtnai1f/bNd" + "v/zrh0TuuOQHD8hfXltiyGeYscUHZ9OtTvPcg3Xzf7A3PRdkSjwwKeX4aWefbTcCda9zf5gW" + "V+gUsW9X6cZt4m6vM7WjDtrmL+8Cn0Q7w7Fxe1oxCG5aI5ipXvb9CMSV4C1cCAsGRyAeVQhK" + "eqVkISmOEgBUKthrUIFUzpudxLfLsGKphoUrSlDkAhKUB7nKvaleqCmXS7pfx75mVfqjXcZX" + "FGH0oK5cb2UJXxpYSRbS6mjAZz+fdRkFi65gwNy1BoPUcP1g+/ZIb+AC8xkE8+aKGumiRtW/" + "nwXRSrKwj/tALigpAZwo+7/UiGS0DGt2fDKYmgSCFQBoE9kJAgc+ml3XEa79AaN7Iq4lYd6v" + "+5L5fBdcuj0HdSeKxI1AoAnUx2LHBxQsK+Vy87jcVm6HlCtvEMkqCGmvvBgy0JgM2kT1CfSB" + "iNnGmVWLaFUY0R5aizYemkVt7NspuETPGBC9+O51Yz4dCBZvolDGiWeteT+pzPwdcerUWrqk" + "xnsk6XBL2JayDy5Rp+avvbHO7KShcY/l9X15M11W9hQVNtmtcahcmVnU+9PDM9dpxAqPGaMr" + "NrRjaDxxrQmHfpptTVVUXDTzBmjW1pWpljUEiHNd78ftXyOaDPHTCiBPvjEiPrXn9q0SQUrb" + "mYP2n00/2z0XddypSdubMu63Ry/5xU1bfi01V4h/c3zBuBjUefH85OW5NT+0ff+SdHu83wPZ" + "Otl6DJWX1lRGqpdNP9Uy19TZuKiCNAtCmS5tpQlJVsiZ6HhU0oimRwcgHzknE5HH85FXVxgK" + "8nGQ8oDHF6AUkGcA7jTg+MISgo2Q7AG4rqcVK0NS7mAAvHFNsgGA83vnM3EBzBkAsKtj6qui" + "bap06STvHWn2D8MnzE3o6DBfYLvIwL74lhFNqvsB+a3jp1Lx3D1tdm2XXzc8fTpywYenwafb" + "OmOmTq2etZuRmfee+tbhRcVfX+0/Ep7qjl+58OmtiRgmH1xM1v1M25uy9uNimj7A5HFXzUTV" + "bdIbNdY/f97XGOkli1kvkPEvdFL27i62SalOXSl8a/za8N6MRP35Fzn75zXb3o+cOju1U2J/" + "Oc/3bdXF0UGuuX9a7tKvdSoQjwkUYGCY2tu5yVAY0Nu2SYCLJnIodiXxpQDUTVMZAgpIdtID" + "ctPgMvAlAjNs/RL4UhlIFr6m2SJKlMuu5hUj05JQmExKLt7xiUxGDsH23HjgcbcAVf2PzT7v" + "f1ty7jKWR3t/N1W96P08zdXJMbh45mrSE3SCQCrJJQrJEB1Ky46sQ/nSXpQM/dMkM407N1pY" + "A5sgx3hAG00fQlFGcetXdotR1bvwAjRXB2RFvk8qXvya5cdNTJQRJPAnMrDSN2DD7SfvMrFU" + "2lu+OhBY9VONanVYqhWVd4mrR4HqzUkaqu50KR+SEOyFnyxISsfIKqPgcVC9H31D3tioQBsD" + "DstA5YqpCgKiM2ZdHGGUnAsCPhfN5CP3XvqEVa69pw8jhGmUh2qUr5fIbqRLFcZCpYtqlvEj" + "WUT/6HheZYIcloA2QpjREyGU+sOSXgthbxh79EtxvNKU2smSJCiMJK9ZwGOZU99pyMYbS6G9" + "w6spBhx+iUObEy4Kdw0FV5LATy/J3F1yc7fgJiHt0ELEOoqdAi9jU24DG/+yYs9ScIkzN/F1" + "XQ2ArgaYaW/b0IKVrL2/w0OSy1ozrXFJDtBIFtXIlIikN4z+wCi0PhexPsa05cZ6q97l75US" + "TCWEkcJuQ6gPRuQ0fjhdZ7SvMkNKoAFzuq0XEea/b8Kq197PTYaWsRfZfBEu9bGD2Un2zyo/" + "B4Jz6Xl2JSmBHesoTo6bf3CkOI25aDP7QGRA2Kzdia8+/C21z1iT8cBkw4hHwWsKCwKyfo6v" + "yHhJTzQpOfh8Ts9O6xg4bYZRgG8z1PyB8/XsK+RhHq1nnHDCwwLfSWHujhFYRO3NfRiRV4M4" + "RzNAYlXkBSUEIZllixgpCdEkoSRcTkd8+S9Zs4GHOg9TexTRxPvsXTRb/kYa/0sygzERfOgG" + "Mn2pV1CB7LD4lTe7Hjgzj9g5JwAAchZN/HPl9HtxWCjtDV+deg2Y4wFT6RlcUIPmaHdHvb2i" + "spuKZs/e6iSMNnlQ+yvRp8mjnH5u3jYicfeLlz83fNeVNs5WaP3+t62PouNmJI788HmNcHPV" + "KE4kqPd497PmTMuYHYTMs046c346/ghLg8/qcaYvGprZmiGKfS6SCa4KpF0SksoFbZAcFGhj" + "QdB7Fr3c6jYLMGMBs7v5Vsc/gY559Y2iBDkQWQ+SSFuLJyVTlamid+xd79lJMsfg57CSBOQs" + "GJ0dHGbyDsPkhs/wB5jmX2tzw8U0bYBp2t3+TDm4ibacmVuJ7M+MqpuaEpbwxsGkIC2l7c3J" + "P+w+n8o+kxjhelmvs9Jq6pfHFzjnPjwpu/aslVF6LGY5evHq/KLSL4C9s8ut9G0hzVgY7d3e" + "fGgNvf05Xo4LZqZmgCSyVClHoCtJpZlAZFR9JlFDuFUfaZ3iLJJLaQZCmJbMELWmwxYJ/vSP" + "71EEaWYyIv+GyRzHyKcC2ASCTUYjyQwlABR8cZNZ1bQuxgLh29sPxL1JVsttcAHFDSaX9f3H" + "E2nSInreOcKY/fWTdFydsoNT2OeDTImfarN+TCBSi8pv3PXfuLbqDgVZ3ZNdy1we1nf9mV9c" + "Q57fLrcD0/vuMzL9bnjfazy3ULK/uL7obdPdsddWrv/jWcnSsnM2tXWeZrdW8EEspfZJwTCi" + "hBfzoKue6k3fwWErQItKigQlAtXXPoGgUPC0MFDpk6rYSfraDX3qtlKC+/wofRdWyMFQVmdg" + "B+jZ084CrkbZbQt5wZqKla59JvAfpHv9WzqBhFghBVYvclPz/Kkf3RVFDbKlx6Q06l/uCsIJ" + "GreHkKGSTYAE5ShhQb7N7Ldnl4/Bysa328cZyTSyOQOyx90S64noVEB84fz+V1vyTMbWrJgc" + "KZP6vnCO3/xyERJVOz2k+rGX+5Ik19KMcuOEQtrT2DPQxRO2xrBJyvwNZz1XZURCV7EI+FIC" + "nH6pQQgcyuwLCKBqfYsa0eeC6AAKCraxyMjfYBdaxFYYZ7A4XbH0AKjqfWSdkRhtlNFcSd29" + "glUvVKnUAY6nhzRh+kksgPaGP4yMTAPgr9nNi6oaRUAQW8TLiEZHCNG1EPoVUXYzFd+DJayt" + "9J4TOdIalhBGEXtlrLc8V8pA7FAycMXYhnaupyUFK157Y1efcVn0i3cXjMnEJZ4xKL7jMzee" + "yycYkb4aCb/QIFQQT5Lw9ISoBYSKOmCkr5RdDHN7TugAh0Yj04kB125i9Wpv3ep94Ih+vaaF" + "v9Tg0hs1aHP1KpujA0TRhPe2BM9ocy/m3AM5raMRy+2c6D1lJfQ201Fjfw1o2U3ZuMwAVkRN" + "PVqdTzobYyJ1mDle77zXpj94x+5Q5+d/rVrlFPXmQ/3IFVKDWUuCG2fPLOrEcLlrb9/q+EPr" + "57r1LMEKF9eUwYVcWaanMoiQprdOVs4nQhKAsAQzR4uKzaSI6DHNURkzeD0JjH1d7693SekW" + "t1I8l8UH+ltHyrYplmZ2j/m0w/z7gpB5h+0tx4w5nm4XNwqLor15q9fDwKb2lkNENC4Ub805" + "NjeWy43h64wkKWdAH4qQT7xYORzcwO1AJOgm1doQZcBpylpBI43xJZocxOsELm+wM/L+470x" + "Vrf2Hv0fAlGwZh0rUlXeRUixQmyL5QDYnQ5CClqJvItA2k5/xpMDEjSOHpfeyqPX5Yp4eTJ4" + "2VhYzGM325deVuUZH3cz5p4ig9gDBXd82/KBKk7bja4vuBh8h2JRZQJf7cAlmYJAJUWCWjA6" + "CY72QtSN3tNAylICMw0pfcBxFr1HCktqWFAFCgD3FtFqc5+8zcNK196BjYemDRoin4RLuqYA" + "VRAtEYnoYoBkGOldVKZSvwFM7dtEDzCqem2DKD+DZX5E9MJ6rhys/GgIsYrloAVkgn6tIADh" + "EheThm1vA7H6tbfi/7CCQzQrWKyKTNQQ8cEovnNk1qnavZV+CSdJsWDiQpN2y7kjOcYbRrjt" + "zoI8eCdMFyvZVs4V99YjRck2vzw/zYv+AswwHz0viesiwUJob8aGQ4NQ+WehdvtzZnPPXj/V" + "mw5DB1KpUglMIjG+AKEsSglsTieu8DvHBQhGwNJTLkfXpZoqsQq1d1uSeqX2K4yU5cfjUmg6" + "qJALpJCEjt7RBhwD4FqHwa7HqXX6WEnae+h/kGShCXjR3OiO6HhCzJpzuhQr4ChAOfXjjGJn" + "rCrtndJgaD5a5emTcKly1AxlF18qVu1akUnAq2S6jTPDL5wbH6lye8ABsCEyfFoJnr9iJE7U" + "3vTUPw9kTvNqg5bgkmiv+eACRI2JfGkrQZge7FjcBpIRfquIAPTOp83RzQujY9Vp72MUQJMa" + "PSxtrcelDtF8gHWyAimD447Y39KD3QACwBUb3SXspH/E6tLep4ZRltTochpaAakkkhA0gWhj" + "keTZ3DAat4NAnc+YGM7eTAAKF5uLv76cW4DViM+HcB6sYkYWlTSKRI2iyka6RCYRRvWwhQSi" + "LfA7zdzbp/dqB1ad9laj/pnar45M/OP9sNSpPkHVXLPnFqcziMu8vBkMb4ZfAgDU5Zu+EE4G" + "D2DVaW8k6s9uIA+Z+NLkNS51Y4cWbrpUYKiu95Eo+dGJQJhncxN5TQcAg0RbbrTcRSJ7TgJ+" + "sLI3Oj351V9Yrdr7hbq0P5BvvIoYoV06jvGL70QigAPrIwHc4zA37c6uSCAE0oHoqrVr3mHT" + "d2ZMNDY4T9TeL8hDCu1PP2bgUmipUXirsUpETEunVJJUq4YEJPjAS4stwv/EytLeM4bRr6KR" + "NVJTn0viS2UEUiiyyk+UkGfWQ82mv1PNSWv+bXUOClyqdkn+OLJwP1al9h6inooDHoJeFQfh" + "Ukn/V5WnQShNh8BUQ4oH8Nd+RyshiUTXAXQNR0zeeSYbxejz0N5AhrHVcAvsaxGq/+TgsVp/" + "zgWxm2jjn0WJpyV4tBFycpxsNu8WOrNjF5iaLrV3LzEeZXv0woVRP59XrOZY/96umxvVxhwf" + "23L75GgfkVe65UGp3RGK6eEtWBDtvUb9QZv2g2TsJPjgAhncM9lVXTcGqGa6TYZpkyPvjJm2" + "IdTZTZeyuKjmXmHIcSvnMtOaat6KNQfnx2SNuObaZl66lVB51fIRKLM5gtWtvRcNo7VHo9tN" + "M0FkAimfwEkFJLYEIQqFhwNpSiuGYhRQUkRDAnjcRHVLEwCaqez4eK05Dbrw6DFWMT5nshw4" + "QOjkTMCleKhw+VkVXSEJwUIYYCHshSCFIfTOEKqjVoka71XKctOlRZCEZSNkeUAsJSNJKZLJ" + "QYmUDBmOhSBqFQjcW2k2M+lm01MsBb7CJc7CsYZiqFuvd8AjVMvRXmhhQdppIUTHIRnp0gqV" + "eishyxPirUKSFdFyebpUShNKliLWiIivmwHE19sGZ/9UdhirH99maSC3kjaNaMKlX9MBIxff" + "kwkKpYAjh+PI6eaQlOnQB+4yXp6U5y7hbRXyFBDvnVXyA/ck+V2ZDJZIYcgwG4IYE1EWYJZv" + "8U/Fy198sAT4ji0HfOX5+LwjuAgGSxTj7jKN2GwzktR4933D8sxdNbNcnZwmhyX88HrkTuKk" + "3e++ZLYdXhcu9hntcmCW9/i3q7d5rnSeWlFde2liaGerffbPXg7Cu6UOF5RJyfZzbowsH8t4" + "ycICaW+U6qVs3A8kDk2Ah7OU7SoDjftP1vYmzNwrQthjmtL0A7YzXM9f+FJSVVt902vJK2vH" + "HX6zK86/Arnx0Mu/r87vwyr+nxYlNYqnaRSL9fgMVfRfukPWQbFzPjGqpxX59fhmYF5GOfvm" + "qLTQN+0zde289d9GzAqO+KE0LWDM8hr331sdxzURDyfufVJuf1nU0p1Lq2u0m7mzssIRC4Ov" + "Jonz+EMDM9iwM61OU787FV5LGTt5g6vu6lOxhs2i5VmU8s23d+xtWLHDeF8I58HIHIMD/Ji/" + "7r9YvDZhEm9bd1/7s6iQ5y20BBilMNMrTsIND0ZsREufTsHweGpvzcNo2NHwDHaz+tepKxNm" + "qVI2OSHtWsS8Dfuf7dh8KmUPcRFbFhP4xXr1G2lqngRemHmZqhscxx5rX7a9vKTdsrrn5uSC" + "26NqR+SUP3x7+FDoolnOU/JN7b+uWHNBBrhs9BdUF+99giX7n7YiacgWD7Ya15Xp8dnU4CbY" + "IsE53uXy+VMUC67e5eNIlNDAM4G7UVB+z3kkKTdCOKNu+soj2Reulq2IHW33hmy0dvZVtyA0" + "6kjU8v0v/H7Z9HmV8NDG2lGHoYLNReXvdd33+v352PSvX7Bw/9NCpQZuuWbYmPqpbLM0KQ06" + "uTm3zfBGvldGTLRV7kWu1KPNcMm1cRY1EZRo+aFo5AF5dwIpp6N5ctv83mtFjYqpntefrA6i" + "PavdWc1fIvBceNZwOu9R2I+u3Zc5Ywqr4hVbsq+UA6EHPa3uP8mkYyG1zwHgocDBvuzboBWk" + "z6MJumrTDR/KAXKlqXYS0EfYvgt6vxZ6l47IYUEtGVKQi0vtso28GH5crlwskikATvcIagwp" + "vwZ53IsY3Wb6nYuVd8PAvNe2NydczXbHcuArWg5U/xbPcNMuZGs4pg7GjMoyYzabKmxqoIxv" + "Mdl/fKJYN8g5frcx5VJrnE1zJP85dXt3eXbphhXjoK7Lz81eHLX6Mcv1dlPKl982eB0ZFxZG" + "sTyMfAR2FTg9v2j8CZvKe2qfEahL/gMborUs67m4WAY7fefUjzBSxQuwcVLChefvrW0NWWIx" + "9Qjo8fVgYZbuo7TCzBWHLlZTqs5W/ZX5KrgruzKNLbnlKte7c+enyZ8pucJ90Hc3nU/UBo4/" + "eHn9roltf/Xq5K7zeP59WHI8Fkv7NEE9KgPppsLV1RgX1lCi062aaraSrgBh+1rofTrSPKID" + "MLsEzlQllSRhuiFUATMUrKoEBZXjTrWLjV8f691SIxOpK4bA9Qm29+dUkFKxBPjKmwOpmtfm" + "rxdxEQyWmM0rmfoi9R6r4uPjp+c/Uo6FQB0eQWk5nVGuzZ3tzJGXZq5zctTbYMR5VH4w4xhy" + "49brzWF/psX1HCcd6wUZZPqXMuaHRiyC9onCf1/vHpXq1l4VQm5q7I5U+4fiIN8dpy7u+fO2" + "8aRJ4bOoSX4thVamO05Bj1vd7lfs2nOcyxjh2eY0usy0coyflX/HvfcGtZa0H1eRI7uxHPiK" + "nwPb8XFv7mpXadZwLNCskTI9EQMiiuBRs3bo7t234UbWmIPzR+U5rL7hFXk6qDgD0ZFe/bs3" + "3bLSos0wc8qYa3rXDHJmB8jfeD5d6Vo8p/U6/bzNXBdnB6/Yk6HyyUcW3plBotW6ey/jp03D" + "PiHipX22MAzb0ZAtGSALqb9q2N98HUhMKwcXJKzczw9mL6TrPGbYKRoFL5f7OKX8pJd+f0+L" + "vofD8g0H9t2sqomRzgMXV7+FXn14vJE7TRDs5PuHy97pzZ1xf2Z2/vb1p8CpI43+0K1g+nP9" + "7D/mYun+pxVRjN/M7F/TAI3TzRS+4vTHAUEHoHMptamLJCRPhWgFiJLBVAUCdWtnVyyU3sC0" + "/TvWm14jY+dIP7MA4w92/DF7p2FPJr20Tw7UHAN+s6ezYxUujpB/+6atpFsVzGqQLzQBFQU3" + "P+DxS/oASRcIsYwYCh4Q8prGvEiLiWfdakzOKdllz7luJyH4CnNR4Lzcad2pjR3TsBDam7/J" + "0GCYbeRod9SigfhucONW94tqqiFpUq/TpcuIj0Rde0NTuL+sRvjmd0g/bHlJzwh7CsdajNtz" + "KPhq9bOze1raa14nehyRrDwV5NwxhdFy/cbdA4p9WSLeE6eLnQSbPFdvvm43CYukfR5gPhQX" + "6J+vL8aFtFDTXTbChE9XJW3Pneonf7C1vRvugvibtofISFWWyLEZTbL6e2uaQ+CSaUFPHUIc" + "rurO+kyr+TVnflhAfkxfno3Zd6mchI5VXxrgy5/Pv7+g32tyZw1vTwurx/v4mzOjsU8zeWmf" + "FqgDw0C/R1fI5d+HE/LW9jfOIcHSMOqlp3xdqt0vY/Ky7oVcC5/Z/L152gfL3eykVy/z25cE" + "Hw45WrUkZJbTFb7NeFT8Wwu90MF7dPzudTe90EnTmBFiz32zyDM9R6x4vsfeM80ziKdH34Ul" + "w3eAMHDkd+kNV7sjHMZv3TfV+9yhzMCFx5eC3QQhOZuMGPG8RY3oa0Ci4BlBXwXNShao/wyl" + "ORjls9AagZQZ2Ucu/p32jowou2mqVEvssBX+OsofS6B9ZqC2o4GUjTtilXa5jYZgKBxIlCqC" + "dlBIhvQgWJ8vT9eRBKp7tep5iKjcTu9Cu5jrweLvECExt9ph1a+wgPfAYxk5voH1iQyY7Bs3" + "97nnhGAsBL5DhIFK1YqQ73i4IJgaiBUNMtRf0v0aSqe52/JuNxoF04oz7MwFRFbKA1tenUzu" + "I3ndDrHkhCtH7C4Sq9/oYQXjSwIG8jH9V4fu4xIcphFcg/Kl5G5QCEMwYp8UzvVWcAQlfTDF" + "nXEOjeW2ouSS6DUM0p2PIDN8mm94H1wcULykWA+i8eP6GlSf0YstDntObHOtxWB4a+/45kOf" + "u2BNge/wpr9EkSoFJbBq8phDZMQJsQ4IZ0xUtnPjUbjSDlJwW/gpXwWI03No9jYZTI78jUhd" + "yK/vlchBIEk8gpvpduoulkB7V1fPnIHUfvTeD5txEQw9PCJukPV6Sz6/hv5huR9BKUF9CT2E" + "bBGU7G2TfY+1zFtZIO3dIIQVEOsFoRFwOOw568RyrGTtDVy9SAfO+EwtdszDJdljaLKrG98r" + "BFLCJtUGpMYQeg8L5CDxAo/LFnO7yOwtbA/YSvCFv0PZ7AUAJ9tt9MM3t8diReMzbNt+0cEV" + "97NwiR7KnmpUgZL8lSDMTYcZfklJfGlXDQils2xVU4VQaRReIBDYTRCe+doqalQQYm6lppOR" + "AAbIbFSw2DuVT1ThUqbPyFU2/4LtjvbW3qXVVjawCymSBzYOb8bXKARScjuZRBnLTZSV8KWt" + "MMhYp3AEqiGoPrzASs8LfCmWpwOS6NLfU9nu9kC9TiHF7SvtKxm45z66Mz8nqRBLoL0ZWwCa" + "yjRw5I+vuAimDRHI0qW5ew0luZOshDC6HLFXvia0WQhT2UlyHshxt6XxY+Oc6nKcnmWfVDKi" + "WAaNtvbnCmnKWoaitIzweK5EVvERBgw8fMvNzFdi80Bv7f1XHYAGMgv79Kd8XDAzNDDP0VQp" + "rRsW0rJZqgmjUI9JirQrV18o4Y1FctB93Ph0Kc8WepdQxwYcH1xOLSMiNFKsgTCdNxoJUACP" + "51MuXpeTgfsEj6ziSbaGWBztzdh4aJX8lrFSu0Zez9A8pAgYOj/xqFPXIxBdqfHujbmtrjmv" + "ai0mrzDZvZq6e+ucy9S38M/yQ6OEm7MuFv7lveHPtlXBa0LCY+pvnbWZOippxJ2Q2VLynLSA" + "zth6Wsz5z+ef3L35nPxbo7u1YaLeLCwWvq37QJnFMPlQFS6sKUPd4dnSInsJzVdI2wLRlEw/" + "RY1MTpNIedBeFLF+gVg3I9YFyCGYeY7FbU0vhqH33M0KU4k0DIJzmX7cWHk0GSgMp607+XcH" + "tlzhrb1xq+MYzid0NChzNBsPdbnCDGwMh49Kpk3K903sWLDP/siJctE/tTzXVZP9/SgjiDOc" + "vgTJQwtM5xhNcQ19x/n+4G0v40vVk4lFx3+i5aUHHL0T0uc8h+apBFl/EZmMt6ae2EKkD74K" + "PM7mdw3UUPc+L0fKokloPkLa1v7xkdckyGicVDsOaAOamoI7TUGUwNgBBL1jb+aX9FIlYk8h" + "OQmivQM+Z1nMtzygg3UXH3yn+QMx7dUlpGI462atuqGcQeXMDNsXXktpif8rzc3ZfYT+owTP" + "8O0hwYyM/LYtSz1JGWOo/ia/U9c6e2eMvm5b5PC9NWLs5zi589Mh32TkcrpN1uMLy2evWrr0" + "WCKIrJq4Z/MP5HAslvZO/x+wlmrO8dV98mbExgvHlxGRvdOcys5tJs5riiy/dZXwckJGYlVV" + "m8e2g5z1xkfZDW7GuY6HXMZSt1j8sD828uCl+eFnf0jwEC8MQnq6L8iXn199YeXOp8E+H0/B" + "3UmExAfebmNXzsJeC+GD70kjnCffGryIodLfQOPH+h0dSM6ZNM7rVd7loXoGeYlSbrDkbuzo" + "XEba+AfBmya3XyuLmDudEL3J6VGOEly46ZT3Eorj/rJue9ov0+2XX7DoD2//Dwi+ruf/FrZD" + "NGU/cEr5MqIwdt+JOwvCRxVIbJyORwh0wsDq1Wd5odva3I4bBj/NzFPoOxzdiP5YiDw8vOBJ" + "8FHzSoNWw3BaW6P/lNnHz375be1SOfCaOU6Xw6mjYLG0zxTU028g19nltOA0LqxBc/W/K1af" + "VHKaaJPY82SudqccN6exR3aPtBbX2AbN+iPICCbmR82Zmn1MJ4O8X3xdKomf8uBD+7VR+877" + "esYf3uUHvfSk+vPaHxjUnh7Z8vF7j3osDr5rSAYOWBo8GmNx4QzdPvA8UiTLFfOlYbsJknQW" + "CYKVlgw/BSqQMsyuOEZ/egVDEERfJBLJJXxBby6AXEDBaBLUYlSdLlhlpNPFKT4G6+tCLH6c" + "Yr0SAPx13A+2mEdhm3d8tE8ZhpEBaagWDT1LY8ynU4ObQnRcxbPG3s1r4Ve6BtvZ0Fv4hM8M" + "ZuImS8VViSwYFPuXbJSMg+eZTfveZl+NS+niB22TE1YluW/QN0122IjaPt6zIPbz6mmfjJ8a" + "t78w4D31WkexGo99mMNH+7wBGlpXu5pjtWswcff88k4dV9ia9owyQxEd0m2KBUO74u0FOZfc" + "9B0V+ZUOUdNPzlu0aWyXt6Q5FHHpnVC1ddkp04TWvuz8pil7Nx4quFad6BAxZzQzDasfX7KA" + "c9Oj0T90fwddtekhqDY97apNTw+L2AaTkcfiZkIWzFCQo4tb0BrVLucc5wGLK8zyB2p9+RtY" + "qq0+ibSV/op8PjAxgPoPCszZJgOA3H3eJxzCbv6I4WEytE8UhtEppgEaurcgUpQg4pcE8qOP" + "ARIe44uLSFbDJ7gqRXypAJCQSUIeBKGCUoH4d0Hg7wLBUr7AsPQlCMxfbfKo/sHTb1wUwcDX" + "oIezbUwjfyjP+U6SwBZKEQOOO/USY6IXv7GLJETHQeh2RHmboQAc7IWoIb1HCXB8kTKwliT8" + "ZCEE/SB4GbB6rNV+4T2F5TcI8D3eNBDDKscUa9eSrSGYqiFoUM0ommpGserISDLDnitXtAKS" + "btW8UsZKYIGcETnOl4iuSMsSvEj2Lo4mee9kUbvBh+fA+jigcyXweAsfVUXQosDM2i3G478B" + "o73/D6MHQwMzeIi/9q4qdKn8ZebTq+G1+vb1J5aBcTGmG8Zyr5nZhkZM6xq9q4R9w43adlta" + "/nL/6sxT+a53Sn4/faV3X+OPv5IiGyoW7zx8PxqKcrmmbJETTlf71q1ZFIU9TWUytE8FTIbC" + "sWLhLe1u4NMw/atjhs8AOE0T7G0lcyZlOXqntAK/7p+TZfBgoThm7yz/G5xwyhZLy8ixCw9J" + "w6ndLtTvikfOPPGLZ0aL1fGpM1Y8U07+48Or5MTMJce7a9rgvW1nrxHSffwclnsTx34DTft0" + "YBht4ho0zdPVif2LX5oOSFgEIUqdSAhWugMlLO5pFnt8/8WC7MBuXQSBENVG/CJP9Ydibdb6" + "75424xu6tff9YXSYaXRz/nXEx7dKM5roZutHCUEFHQwJulWIKiAaX7/Zyt7dXbGeuIcFlHwi" + "Q/XcKsiJ5jbR092WuyJRBAPMuBEnwGnWJ77B8D89GNAwDD4xG3JXXb+nEkW7x4zdlb/0CTe6" + "cILVrEWFE+TEKQaBXtRwpu2VbXsKM4wz1qXRqjYZnzK52rdu+ZQrpypGSX6/+Tv/IXDQ4VGI" + "e6nly9l/ujcd35FpU1I4/uFtj5NzvgGG7xIynB1zmMFhVQik4E5QAr4iCSVkgZxA9Apgn0PB" + "atTd8cGuAOAFWN3A4nK5oZXv6ND+t+S2kaHFC4q/wB8MgXIz11fWW59hTzeYDHw2P8yg/N3Q" + "iVoyX+rerrJ52IhxBG0VNeamPWHZcjvIWSiPUpUuKLfKKFRZPuEM2gBU0djNhBQegyum+mxX" + "30VCOiSBJp4IVAKSf1Ay4H86gHLe2eE+lgnH1WPD6FLQMA1uaRbfGdzScMxGWRFCu0cbh0ye" + "m5LlMT2GcHCUw273N67HlvUGPJ7qnZEufGIYknOt5MEGS5cd5cssP+4yKVAe//SHTvKty8RL" + "mcH3qgozrn3QHXfLr5P5+n7lN7i0TwGGUYrUcA2my4vr+1NKoijXdVala/Yrc4ess7pzHnHT" + "JoyKvM2Zdcq79nyf57oZCY6jIecwp+mEsK89fKeDncyVAfFIYUdO/Pfnf/6z7fofJVBr8tb6" + "G0euzTzz5vUd1rv53uZnR+v2fAMOX2UA5+13E0uPbb8A/N87OuXoVYEU7b9poT6yDhKjfHnu" + "JlAyo7hlBT3ZqKqmRJ4QC3JAkMiP4vaVy7pJgEUfM51+goc9VWfiuIlsGCcdGuVDYQCVE0CE" + "RmqLc40Tqp/OVvKlMBUFoukoGKl0jHyvgnoGifvYdXDVO3Y2oE/pzpPRiuUwSRc46uP9PCQ/" + "t+wbDNpb/zB6rDQMfv86RePRJLwAIS8J4jWzO8g2khpPYcNaKPcJknOFeWR9bMeK2PgVMXw+" + "SZV2ruGOiyQCoeVI+IWqZ9u/IR5fLz/OCipm6vCKpDyWhJcs5HF70oukZJaEnCysUUA14lLB" + "UYFgH4FijQTeoXfQfCW0OCHrHXD2kWlyHX3HnW8ox9ecN8yPfagcxsqVVthKWIwvjjUyTo4U" + "LFCApsTj25Mg26kQXVQly4WzlOeYCm6MHAWBf6TmaZsTEOyVL0wcF4wNo1dKo/r/Hol3gSCi" + "2hKmRrZQqrKpCjgD5Se0kYU0tgdNIO9uh6FcmMf1hqv62FtZkTCYSgCJFHrddqgabqpLr92l" + "BICK91PsJD/tXPANFu09fBidKxqWiCGWdrIEhSMVQBYMbACp7mwZazcN4uknMxRyQExjKbmJ" + "UFWDQO6d9zxZlCIQCFIJRGt+D5oeHietpKkm7NZzU9YtJM68+A0S7Z3ccigM/bOkvHo4ozJw" + "Uy2VM3OPQ7db4P6mjijH2RfDDur4KJzGufhfPbel8/KOu1OK29iC89nbPj84Y95jef2w31/m" + "zmusvc6HLeCNRHcqCH1LAyPOn7nii2XBcZeYuhA10NgxP/TJZFwsmpsw5LFimSRFCkL+wHF9" + "IZnF7miFhRIWkgM2GQkD9IQBRsLtjGXk0C0m0BZb+jIxoOOCIMoEhuJ7wAE4scPWNz2yyvwb" + "ENrb9TCuRsJA8H4XqOKSWTdHxnJVgtUsvtyFyiO1vWYr7DMawA0sagM5xuoTiUTNA9vQd5RN" + "7Hc8tgJ0VXarPO3By5FVphGN2AfkmTjuDVPnhzifMtJATNXUjEYYqWZVcNOMoGu/Ljp9hbPR" + "7sW1yTmCcWK69YGI0sTe2owRZ0x/LuieTLO/evF556g5RqnjJPmiVzpmDhH279A33/+R95QV" + "MMXJfn8sFPANGO2d2mxoWqX0bdDungTMiKC9oARNV0Wt6mwaogzsolED5ALpBCpKamtgb2kQ" + "9Gyn/02OSaYLBSQSKETJ/C9wjJjeJdShkgBDc/Ziy6lV2KekmDguChvGUfD/MyIqiG4VBDky" + "iZplIygwFqA+yeo7SdhXi2AWI0k1vSKT7DP+AbuS6AdYeSIARGHgY0Daoa/UzF3FqsQkUkmT" + "kAFKXhg435zw+t8w/wcqdPzO"; + + +/*---------------------------------------------------------------------*/ +/* Auto-generated deserializer */ +/*---------------------------------------------------------------------*/ +/*! + * l_bootnum_gen() + * + * Return: the bootnum pixa + * + * Call this way: + * PIXA *pixa = (PIXA *)l_bootnum_gen(); (C) + * Pixa *pixa = (Pixa *)l_bootnum_gen(); (C++) + */ +void * +l_bootnum_gen() +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +void *result; + + /* Unencode selected string, write to file, and read it */ + data1 = decodeBase64(l_bootnum, strlen(l_bootnum), &size1); + data2 = zlibUncompress(data1, size1, &size2); + l_binaryWrite("/tmp/data.bin", "w", data2, size2); + result = (void *)pixaRead("/tmp/data.bin"); + FREE(data1); + FREE(data2); + return result; +} + + diff --git a/liblept/src/boxbasic.c b/liblept/src/boxbasic.c index ed26642..6cb93ea 100644 --- a/liblept/src/boxbasic.c +++ b/liblept/src/boxbasic.c @@ -1978,7 +1978,8 @@ BOXA *boxa; * We are writing to file first, instead of reading from the memory * buffer, because the gnu extension fmemopen() is not available * with other runtimes. */ - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (BOXA *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(data, 1, size, fp); rewind(fp); boxa = boxaReadStream(fp); @@ -2072,16 +2073,17 @@ FILE *fp; PROCNAME("boxaWriteMem"); if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); + return ERROR_INT("&data not defined", procName, 1); *pdata = NULL; if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); + return ERROR_INT("&size not defined", procName, 1); *psize = 0; if (!boxa) - return ERROR_INT("&boxa not defined", procName, 1 ); + return ERROR_INT("&boxa not defined", procName, 1); /* Serialize: write to file and read serialized data back into memory */ - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); ret = boxaWriteStream(fp, boxa); rewind(fp); *pdata = l_binaryReadStream(fp, psize); diff --git a/liblept/src/boxfunc2.c b/liblept/src/boxfunc2.c index e7177cd..4ca0fa6 100644 --- a/liblept/src/boxfunc2.c +++ b/liblept/src/boxfunc2.c @@ -1022,29 +1022,43 @@ NUMA *na; * boxaExtractAsNuma() * * Input: boxa - * &nax ( array of x locations) - * &nay ( array of y locations) - * &naw ( array of w locations) - * &nah ( array of h locations) + * &nal ( array of left locations) + * &nat ( array of top locations) + * &nar ( array of right locations) + * &nab ( array of bottom locations) + * &naw ( array of widths) + * &nah ( array of heights) * 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_int32 boxaExtractAsNuma(BOXA *boxa, - NUMA **pnax, - NUMA **pnay, + NUMA **pnal, + NUMA **pnat, + NUMA **pnar, + NUMA **pnab, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid) { -l_int32 i, n, x, y, w, h; +l_int32 i, n, left, top, right, bot, w, h; PROCNAME("boxaExtractAsNuma"); - if (!pnax && !pnay && !pnaw && !pnah) + if (!pnal && !pnat && !pnar && !pnab && !pnaw && !pnah) return ERROR_INT("no output requested", procName, 1); - if (pnax) *pnax = NULL; - if (pnay) *pnay = NULL; + 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) @@ -1053,16 +1067,22 @@ l_int32 i, n, x, y, w, h; return ERROR_INT("no valid boxes", procName, 1); n = boxaGetCount(boxa); - if (pnax) *pnax = numaCreate(n); - if (pnay) *pnay = numaCreate(n); + 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, &x, &y, &w, &h); + boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); if (!keepinvalid && (w <= 0 || h <= 0)) continue; - if (pnax) numaAddNumber(*pnax, x); - if (pnay) numaAddNumber(*pnay, y); + 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); } @@ -1079,14 +1099,20 @@ l_int32 i, n, x, y, w, h; * &ptat ( array of top locations vs. index) * &ptar ( array of right locations vs. index) * &ptab ( array of bottom locations vs. index) + * &ptaw ( array of widths vs. index) + * &ptah ( array of heights vs. index) * keepinvalid (1 to keep invalid boxes; 0 to remove them) * Return: 0 if OK, 1 on error * - * Notes: - * (1) For invalid boxes, this stores the values - * (left, top, right, bot) = (0, 0, -1, -1) - * If you plan to do a least square fit, you must use - * @keepinvalid = 0. + * 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. */ l_int32 boxaExtractAsPta(BOXA *boxa, @@ -1094,18 +1120,22 @@ boxaExtractAsPta(BOXA *boxa, 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) + 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) @@ -1116,6 +1146,8 @@ l_int32 i, n, left, top, right, bot, w, h; 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)) @@ -1126,6 +1158,8 @@ l_int32 i, n, left, top, right, bot, w, h; 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; @@ -1173,7 +1207,8 @@ BOX *box; if (boxaGetValidCount(boxa) == 0) return (BOX *)ERROR_PTR("no valid boxes in boxa", procName, NULL); - boxaExtractAsNuma(boxa, &nax, &nay, &naw, &nah, 0); /* valid boxes only */ + /* Use only the valid boxes */ + boxaExtractAsNuma(boxa, &nax, &nay, NULL, NULL, &naw, &nah, 0); numaGetRankValue(nax, 1.0 - fract, NULL, 1, &xval); numaGetRankValue(nay, 1.0 - fract, NULL, 1, &yval); diff --git a/liblept/src/boxfunc4.c b/liblept/src/boxfunc4.c index 9cbc001..75faf85 100644 --- a/liblept/src/boxfunc4.c +++ b/liblept/src/boxfunc4.c @@ -43,17 +43,23 @@ * BOXA *boxaPermuteRandom() * l_int32 boxaSwapBoxes() * - * Boxa conversions + * Boxa and box conversions * PTA *boxaConvertToPta() * BOXA *ptaConvertToBoxa() + * PTA *boxConvertToPta() + * BOX *ptaConvertToBox() * * Boxa sequence fitting - * BOXA *boxaSmoothSequence() + * BOXA *boxaSmoothSequenceLS() + * BOXA *boxaSmoothSequenceMedian() * BOXA *boxaLinearFit() + * BOXA *boxaWindowedMedian() * BOXA *boxaModifyWithBoxa() * BOXA *boxaConstrainSize() * BOXA *boxaReconcileEvenOddHeight() * l_int32 boxaPlotSides() [for debugging] + * BOXA *boxaFillSequence() + * static l_int32 boxaFillAll() * * Miscellaneous boxa functions * l_int32 boxaGetExtent() @@ -67,6 +73,8 @@ #include "allheaders.h" +static l_int32 boxaFillAll(BOXA *boxa); + /*---------------------------------------------------------------------* * Boxa and boxaa range selection * @@ -580,7 +588,7 @@ BOX *box; /*---------------------------------------------------------------------* - * Boxa Conversions * + * Boxa and Box Conversions * *---------------------------------------------------------------------*/ /*! * boxaConvertToPta() @@ -598,8 +606,9 @@ PTA * boxaConvertToPta(BOXA *boxa, l_int32 ncorners) { -l_int32 i, n, x, y, w, h; -PTA *pta; +l_int32 i, n; +BOX *box; +PTA *pta, *pta1; PROCNAME("boxaConvertToPta"); @@ -612,15 +621,11 @@ PTA *pta; if ((pta = ptaCreate(n)) == NULL) return (PTA *)ERROR_PTR("pta not made", procName, NULL); for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &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); - } + box = boxaGetBox(boxa, i, L_COPY); + pta1 = boxConvertToPta(box, ncorners); + ptaJoin(pta, pta1, 0, -1); + boxDestroy(&box); + ptaDestroy(&pta1); } return pta; @@ -638,7 +643,7 @@ PTA *pta; * 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 szie containing all corners. + * (2) Each derived box is the minimum size containing all corners. */ BOXA * ptaConvertToBoxa(PTA *pta, @@ -682,19 +687,97 @@ BOXA *boxa; } +/*! + * boxConvertToPta() + * + * Input: box + * 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; +} + + +/*! + * ptaConvertToBox() + * + * Input: 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); +} + + /*---------------------------------------------------------------------* * Boxa sequence fitting * *---------------------------------------------------------------------*/ /*! - * boxaSmoothSequence() + * boxaSmoothSequenceLS() * * Input: boxas (source boxa) * factor (reject outliers with widths and heights deviating * from the median by more than @factor times * the median variation from the median; typically ~3) - * subflag (L_USE_MINSIZE, L_USE_MAXSIZE or L_SUB_ON_BIG_DIFF; - * see boxaModifyWithBoxa()) - * maxdiff (parameter used with L_SUB_ON_BIG_DIFF) + * subflag (L_USE_MINSIZE, L_USE_MAXSIZE, L_SUB_ON_BIG_DIFF, + * L_USE_CAPPED_MIN or L_USE_CAPPED_MAX) + * maxdiff (parameter used with L_SUB_ON_BIG_DIFF and + * L_USE_CAPPED_MAX) * debug (1 for debug output) * Return: boxad (fitted boxa), or null on error * @@ -712,19 +795,33 @@ BOXA *boxa; * edges vary roughly linearly with its index in the set. */ BOXA * -boxaSmoothSequence(BOXA *boxas, - l_float32 factor, - l_int32 subflag, - l_int32 maxdiff, - l_int32 debug) +boxaSmoothSequenceLS(BOXA *boxas, + l_float32 factor, + l_int32 subflag, + l_int32 maxdiff, + l_int32 debug) { l_int32 n; BOXA *boxae, *boxao, *boxalfe, *boxalfo, *boxame, *boxamo, *boxad; - PROCNAME("boxaSmoothSequence"); + 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_BIG_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); @@ -762,13 +859,116 @@ BOXA *boxae, *boxao, *boxalfe, *boxalfo, *boxame, *boxamo, *boxad; } +/*! + * boxaSmoothSequenceMedian() + * + * Input: boxas (source boxa) + * halfwin (half-width of sliding window; used to find median) + * subflag (L_USE_MINSIZE, L_USE_MAXSIZE, L_SUB_ON_BIG_DIFF, + * L_USE_CAPPED_MIN or L_USE_CAPPED_MAX) + * maxdiff (parameter used with L_SUB_ON_BIG_DIFF, + * L_USE_CAPPED_MIN and L_USE_CAPPED_MAX) + * 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 box and the filtered boxa, + * box, depending on @subflag. See boxaModifyWithBoxa() for + * details on the use of @subflag and @maxdiff. + * (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); + * boxaDestroy(&boxam); + */ +BOXA * +boxaSmoothSequenceMedian(BOXA *boxas, + l_int32 halfwin, + l_int32 subflag, + l_int32 maxdiff, + 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_BIG_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("smooth"); + boxaWrite("/tmp/smooth/boxae.ba", boxae); + boxaWrite("/tmp/smooth/boxao.ba", boxao); + } + + boxamede = boxaWindowedMedian(boxae, halfwin, debug); + boxamedo = boxaWindowedMedian(boxao, halfwin, debug); + if (debug) { + boxaWrite("/tmp/smooth/boxamede.ba", boxamede); + boxaWrite("/tmp/smooth/boxamedo.ba", boxamedo); + } + + boxame = boxaModifyWithBoxa(boxae, boxamede, subflag, maxdiff); + boxamo = boxaModifyWithBoxa(boxao, boxamedo, subflag, maxdiff); + if (debug) { + boxaWrite("/tmp/smooth/boxame.ba", boxame); + boxaWrite("/tmp/smooth/boxamo.ba", boxamo); + } + + boxad = boxaMergeEvenOdd(boxame, boxamo, 0); + if (debug) { + boxaPlotSides(boxas, NULL, NULL, NULL, NULL, NULL, GPLOT_X11); + boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, GPLOT_X11); + } + + boxaDestroy(&boxae); + boxaDestroy(&boxao); + boxaDestroy(&boxamede); + boxaDestroy(&boxamedo); + boxaDestroy(&boxame); + boxaDestroy(&boxamo); + return boxad; +} + + /*! * boxaLinearFit() * * Input: boxas (source boxa) * factor (reject outliers with widths and heights deviating * from the median by more than @factor times - * the median variation from the median; typically ~3) + * the median deviation from the median; typically ~3) * debug (1 for debug output) * Return: boxad (fitted boxa), or null on error * @@ -814,9 +1014,9 @@ PTA *ptal, *ptat, *ptar, *ptab; 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 variation from + * First find the median width and the median deviation from * the median width. Ditto for the height. */ - boxaExtractAsNuma(boxas, NULL, NULL, &naw, &nah, 0); + boxaExtractAsNuma(boxas, NULL, NULL, NULL, NULL, &naw, &nah, 0); numaGetMedianVariation(naw, &medw, &medvarw); numaGetMedianVariation(nah, &medh, &medvarh); numaDestroy(&naw); @@ -875,8 +1075,8 @@ PTA *ptal, *ptat, *ptar, *ptab; /* 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, 0); - boxaExtractAsPta(boxatb, NULL, &ptat, NULL, &ptab, 0); + boxaExtractAsPta(boxalr, &ptal, NULL, &ptar, NULL, NULL, NULL, 0); + boxaExtractAsPta(boxatb, NULL, &ptat, NULL, &ptab, NULL, NULL, 0); boxaDestroy(&boxalr); boxaDestroy(&boxatb); @@ -925,13 +1125,94 @@ PTA *ptal, *ptat, *ptar, *ptab; } +/*! + * boxaWindowedMedian() + * + * Input: boxas (source boxa) + * halfwin (half width of window over which the median is found) + * 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, GPLOT_X11); + boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, GPLOT_X11); + } + + boxaDestroy(&boxaf); + numaDestroy(&nal); + numaDestroy(&nat); + numaDestroy(&nar); + numaDestroy(&nab); + numaDestroy(&naml); + numaDestroy(&namt); + numaDestroy(&namr); + numaDestroy(&namb); + return boxad; +} + + /*! * boxaModifyWithBoxa() * * Input: boxas * boxam (boxa with boxes used to modify those in boxas) - * subflag (L_USE_MINSIZE, L_USE_MAXSIZE or L_SUB_ON_BIG_DIFF) - * maxdiff (parameter used with L_SUB_ON_BIG_DIFF) + * subflag (L_USE_MINSIZE, L_USE_MAXSIZE, L_SUB_ON_BIG_DIFF, + * L_USE_CAPPED_MIN or L_USE_CAPPED_MAX) + * maxdiff (parameter used with L_SUB_ON_BIG_DIFF, + * L_USE_CAPPED_MIN and L_USE_CAPPED_MAX) * Return: boxad (result after adjusting boxes in boxas), or null * on error. * @@ -939,29 +1220,45 @@ PTA *ptal, *ptat, *ptar, *ptab; * (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. + * 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_BIG_DIFF, each side of the output - * box is given by the side the input box from boxas if it is - * within @maxdiff pixels of the side of the input box from @boxam; - * otherwise, use the latter side. + * 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_SUB_ON_BIG_DIFF, use boxs if within + * @maxdiff pixels of boxm; otherwise, use boxm. + * 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_BIG_DIFF, the box in boxam is used - * for each side where the corresponding sides differ by more - * than @maxdiff. Two extreme cases: + * (3) If @subflag == L_SUB_ON_BIG_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 either of corresponding boxes in boxas and boxam is invalid, + * (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. - * (5) Typical input for boxam may be the output of boxaLinearFit(). + * (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. - * (6) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(), + * (7) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(), * this is not dependent on a difference threshold to change the size. * Additional constraints on the size of each box can be enforced * by following this operation with boxaConstrainSize(), taking @@ -986,7 +1283,8 @@ BOXA *boxad; return boxaCopy(boxas, L_COPY); } if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && - subflag != L_SUB_ON_BIG_DIFF) { + subflag != L_SUB_ON_BIG_DIFF && subflag != L_USE_CAPPED_MIN && + subflag != L_USE_CAPPED_MAX) { L_WARNING("invalid subflag; returning copy", procName); return boxaCopy(boxas, L_COPY); } @@ -1006,8 +1304,6 @@ BOXA *boxad; } else { boxGetGeometry(boxs, &ls, &ts, &ws, &hs); boxGetGeometry(boxm, &lm, &tm, &wm, &hm); - boxDestroy(&boxs); - boxDestroy(&boxm); rs = ls + ws - 1; bs = ts + hs - 1; rm = lm + wm - 1; @@ -1022,15 +1318,27 @@ BOXA *boxad; rd = L_MAX(rs, rm); td = L_MIN(ts, tm); bd = L_MAX(bs, bm); - } else { /* subflag == L_SUB_ON_BIG_DIFF */ + } else if (subflag == L_SUB_ON_BIG_DIFF) { ld = (L_ABS(lm - ls) <= maxdiff) ? ls : lm; td = (L_ABS(tm - ts) <= maxdiff) ? ts : tm; rd = (L_ABS(rm - rs) <= maxdiff) ? rs : rm; bd = (L_ABS(bm - bs) <= maxdiff) ? bs : bm; + } 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); @@ -1258,11 +1566,9 @@ BOXA *boxae, *boxao, *boxa1e, *boxa1o, *boxad; * Notes: * (1) This is a debugging function to show the progression of * the four sides in the boxes. There must be at least 2 boxes. - * (2) One of three conditions holds: - * (a) only the even indices have valid boxes - * (b) only the odd indices have valid boxes - * (c) all indices have valid boxes - * This condition is determined by looking at the first 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/plotsides, and are named either * with @plotname or, if NULL, a default name. */ @@ -1278,8 +1584,8 @@ boxaPlotSides(BOXA *boxa, char buf[128]; static l_int32 plotid = 0; l_int32 n, i, w, h, left, top, right, bot; -l_float32 startx, delx; -BOX *box, *boxe, *boxo; +BOX *box; +BOXA *boxat; GPLOT *gplot; NUMA *nal, *nat, *nar, *nab; @@ -1294,35 +1600,16 @@ NUMA *nal, *nat, *nar, *nab; if ((n = boxaGetCount(boxa)) < 2) return ERROR_INT("less than 2 boxes", procName, 1); - /* Determine which condition holds for valid boxes */ - delx = 1; - boxe = boxaGetValidBox(boxa, 0, L_CLONE); - boxo = boxaGetValidBox(boxa, 1, L_CLONE); - if (!boxe) { - startx = 1; - delx = 2; - } else if (!boxo) { - startx = 0; - delx = 2; - } - boxDestroy(&boxe); - boxDestroy(&boxo); + 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); - if (delx == 2) { - numaSetParameters(nal, startx, delx); - numaSetParameters(nat, startx, delx); - numaSetParameters(nar, startx, delx); - numaSetParameters(nab, startx, delx); - } for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) - continue; + box = boxaGetBox(boxat, i, L_CLONE); boxGetGeometry(box, &left, &top, &w, &h); right = left + w - 1; bot = top + h - 1; @@ -1332,6 +1619,7 @@ NUMA *nal, *nat, *nar, *nab; numaAddNumber(nab, bot); boxDestroy(&box); } + boxaDestroy(&boxat); /* Plot them */ if (outformat < 0 || outformat > GPLOT_LATEX) { @@ -1375,6 +1663,139 @@ NUMA *nal, *nat, *nar, *nab; } +/*! + * boxaFillSequence() + * + * Input: boxas (with at least 3 boxes) + * useflag (L_USE_ALL_BOXES, L_USE_SAME_PARITY_BOXES) + * 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 in 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; +} + + +/*! + * boxaFillAll() + * + * Input: 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 *)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); + } + + FREE(indic); + return 0; +} + + /*---------------------------------------------------------------------* * Miscellaneous Boxa functions * *---------------------------------------------------------------------*/ diff --git a/liblept/src/classapp.c b/liblept/src/classapp.c index f647bfb..cf9f4ee 100644 --- a/liblept/src/classapp.c +++ b/liblept/src/classapp.c @@ -50,7 +50,7 @@ #include #include "allheaders.h" -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; static const l_int32 JB_WORDS_MIN_WIDTH = 5; /* pixels */ static const l_int32 JB_WORDS_MIN_HEIGHT = 3; /* pixels */ diff --git a/liblept/src/colorcontent.c b/liblept/src/colorcontent.c index 0bbe067..c1f9591 100644 --- a/liblept/src/colorcontent.c +++ b/liblept/src/colorcontent.c @@ -40,6 +40,9 @@ * are not too close to gray pixels. * PIX *pixMaskOverColorPixels() * + * 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() * @@ -186,13 +189,13 @@ 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 (!ppixr && !ppixg && !ppixb) - return ERROR_INT("nothing to compute", procName, 1); if (mingray < 0) mingray = 0; pixGetDimensions(pixs, &w, &h, &d); if (mingray > 255) @@ -311,40 +314,40 @@ PIXCMAP *cmap; * * 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 by - * considering the absolute value of the differences between the - * three color components. Consider the two largest + * 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: - * * the average of these two differences; i.e., the + * 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, or - * * the minimum value of these two differences; i.e., the + * 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. - * Even more simply, the color magnitude can be taken as - * * the maximum difference between component values + * (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: - * * the average distance of B from these two; namely, - * (|B - R| + |B - G|) / 2, which can also be found - * from |B - (R + G) / 2|, or - * * the minimum distance of B from these two; namely, - * min(|B - R|, |B - G|). - * * the max(|B - R|, |B - G|) - * (3) The three numbers (rwhite, gwhite and bwhite) can be thought + * 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. - * (4) We allow the following methods for choosing the color - * magnitude from the three components: - * * L_MAX_DIFF_FROM_AVERAGE_2 - * * L_MAX_MIN_DIFF_FROM_2 - * * L_MAX_DIFF - * These are described above in (1) and (2), as well as at - * the top of this file. */ PIX * pixColorMagnitude(PIX *pixs, @@ -537,6 +540,70 @@ PIXCMAP *cmap; } +/* ----------------------------------------------------------------------- * + * Generates a mask over pixels that have RGB color components * + * within the prescribed range (a cube in RGB color space) * + * ----------------------------------------------------------------------- */ +/*! + * pixMaskOverColorRange() + * + * Input: pixs (32 bpp rgb or 8 bpp colormapped) + * rmin, rmax (min and max allowed values for red component) + * gmin, gmax + * bmin, bmax + * 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 * * ----------------------------------------------------------------------- */ @@ -992,7 +1059,8 @@ PIXCMAP *cmap; *pncolors = sum; FREE(inta); - if (factor == 1 && ((cmap = pixGetColormap(pixs)) != NULL)) { + cmap = pixGetColormap(pixs); + if (cmap && factor == 1) { count = pixcmapGetCount(cmap); if (sum != count) L_WARNING("colormap size %d differs from actual colors\n", @@ -1062,10 +1130,10 @@ 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 (!parray && !pcmap) - return ERROR_INT("no output requested", procName, 1); if (!pixs || pixGetDepth(pixs) != 32) return ERROR_INT("pixs not defined", procName, 1); if (sigbits < 2 || sigbits > 6) @@ -1417,9 +1485,11 @@ FPIX *fpix; 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); - *phasred = 0; if (!pixs || pixGetDepth(pixs) != 32) return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); if (fthresh < 1.5 || fthresh > 3.5) diff --git a/liblept/src/coloring.c b/liblept/src/coloring.c index fc51b74..c703bd5 100644 --- a/liblept/src/coloring.c +++ b/liblept/src/coloring.c @@ -30,6 +30,7 @@ * Coloring "gray" pixels * PIX *pixColorGrayRegions() * l_int32 pixColorGray() + * PIX *pixColorGrayMasked() * * Adjusting one or more colors to a target color * PIX *pixSnapColor() @@ -55,7 +56,7 @@ * They fall into the following categories: * * (1) Moving either the light or dark pixels toward a - * specified color. (pixColorGray) + * 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 @@ -313,6 +314,133 @@ PIXCMAP *cmap; } +/*! + * pixColorGrayMasked() + * + * Input: pixs (8 bpp gray, rgb or colormapped image) + * pixm (1 bpp mask, through which to apply color) + * type (L_PAINT_LIGHT, L_PAINT_DARK) + * thresh (average value below/above which pixel is unchanged) + * 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 pixFindColorRegions() 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 * *------------------------------------------------------------------*/ diff --git a/liblept/src/colormap.c b/liblept/src/colormap.c index cabb359..244e3cc 100644 --- a/liblept/src/colormap.c +++ b/liblept/src/colormap.c @@ -52,9 +52,11 @@ * 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() @@ -67,7 +69,9 @@ * PIXCMAP *pixcmapColorToGray() * * Colormap I/O + * l_int32 pixcmapRead() * l_int32 pixcmapReadStream() + * l_int32 pixcmapWrite() * l_int32 pixcmapWriteStream() * * Extract colormap arrays and serialization @@ -841,6 +845,40 @@ RGBA_QUAD *cta; } +/*! + * pixcmapSetAlpha() + * + * Input: cmap + * index + * 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_int32 +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; +} + + /*! * pixcmapGetIndex() * @@ -955,6 +993,43 @@ RGBA_QUAD *cta; } +/*! + * pixcmapIsBlackAndWhite() + * + * Input: cmap + * &blackwhite ( 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_int32 +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; +} + + /*! * pixcmapCountGrayColors() * @@ -1352,6 +1427,35 @@ PIXCMAP *cmapd; /*-------------------------------------------------------------* * Colormap I/O * *-------------------------------------------------------------*/ +/*! + * pixcmapRead() + * + * Input: 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); + + if ((cmap = pixcmapReadStream(fp)) == NULL) { + fclose(fp); + return (PIXCMAP *)ERROR_PTR("cmap not read", procName, NULL); + } + + fclose(fp); + return cmap; +} + + /*! * pixcmapReadStream() * @@ -1392,6 +1496,37 @@ PIXCMAP *cmap; } +/*! + * pixcmapWrite() + * + * Input: filename + * cmap + * Return: 0 if OK, 1 on error + */ +l_int32 +pixcmapWrite(const char *filename, + PIXCMAP *cmap) +{ +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); + if (pixcmapWriteStream(fp, cmap)) + return ERROR_INT("cmap not written to stream", procName, 1); + fclose(fp); + + return 0; +} + + + /*! * pixcmapWriteStream() * diff --git a/liblept/src/colorspace.c b/liblept/src/colorspace.c index 465f535..eeccd80 100644 --- a/liblept/src/colorspace.c +++ b/liblept/src/colorspace.c @@ -55,6 +55,26 @@ * 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 @@ -62,9 +82,14 @@ #include "allheaders.h" #ifndef NO_CONSOLE_IO -#define DEBUG_HISTO 1 +#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 * @@ -249,6 +274,9 @@ 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); @@ -306,6 +334,9 @@ 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); @@ -940,6 +971,8 @@ PIX *pixt, *pixd; PROCNAME("pixMakeHistoHS"); + if (!pnahue && !pnasat) + return (PIX *)ERROR_PTR("no return val requested", procName, NULL); if (pnahue) *pnahue = NULL; if (pnasat) *pnasat = NULL; if (!pixs || pixGetDepth(pixs) != 32) @@ -1029,6 +1062,8 @@ PIX *pixt, *pixd; PROCNAME("pixMakeHistoHV"); + if (!pnahue && !pnaval) + return (PIX *)ERROR_PTR("no return val requested", procName, NULL); if (pnahue) *pnahue = NULL; if (pnaval) *pnaval = NULL; if (!pixs || pixGetDepth(pixs) != 32) @@ -1110,6 +1145,8 @@ PIX *pixt, *pixd; PROCNAME("pixMakeHistoSV"); + if (!pnasat && !pnaval) + return (PIX *)ERROR_PTR("no return val requested", procName, NULL); if (pnasat) *pnasat = NULL; if (pnaval) *pnaval = NULL; if (!pixs || pixGetDepth(pixs) != 32) @@ -1168,8 +1205,8 @@ PIX *pixt, *pixd; * height (half height of sliding window) * npeaks (number of peaks to look for) * erasefactor (ratio of erase window size to sliding window size) - * &pta (locations of maximum for each integrated peak area) - * &natot (integrated peak areas) + * &pta ( locations of max for each integrated peak area) + * &natot ( integrated peak areas) * &pixa ( pixa for debugging; NULL to skip) * Return: 0 if OK, 1 on error * @@ -1204,6 +1241,9 @@ 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) @@ -1376,7 +1416,7 @@ PIX *pixt, *pixd; * V: [16, 240] * (5) For the coefficients in the transform matrices, see eq. 4 in * "Frequently Asked Questions about Color" by Charles Poynton, - * http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html + * //http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html */ PIX * pixConvertRGBToYUV(PIX *pixd, @@ -1514,6 +1554,9 @@ 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); @@ -1558,6 +1601,9 @@ 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); @@ -1637,3 +1683,672 @@ l_int32 i, ncolors, rval, gval, bval, yval, uval, vval; } return 0; } + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between RGB and XYZ * + *---------------------------------------------------------------------------*/ +/*! + * pixConvertRGBToXYZ() + * + * Input: pixs (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; +} + + +/*! + * fpixaConvertXYZToRGB() + * + * Input: fpixa (three fpix: x,y,z) + * Return: pixd (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; +} + + +/*! + * convertRGBToXYZ() + * + * Input: rval, gval, bval (rgb input) + * &fxval, &fyval, &fzval ( xyz values) + * Return: 0 if OK, 1 on error + * + * Notes: + * (1) These conversions are for illuminant D65 acting on linear sRGB + * values. + */ +l_int32 +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; +} + + +/*! + * convertXYZToRGB() + * + * Input: fxval, fyval, fzval + * blackout (0 to output nearest color if out of gamut; + * 1 to output black) + * &rval, &gval, &bval ( 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_int32 +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 * + *---------------------------------------------------------------------------*/ +/*! + * fpixaConvertXYZToLAB() + * + * Input: fpixa (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; +} + + +/*! + * fpixaConvertLABToXYZ() + * + * Input: fpixa (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; +} + + +/*! + * convertXYZToLAB() + * + * Input: xval, yval, zval (xyz input) + * &lval, &aval, &bval ( lab values) + * Return: 0 if OK, 1 on error + */ +l_int32 +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; +} + + +/*! + * convertLABToXYZ() + * + * Input: lval, aval, bval + * &xval, &yval, &zval ( xyz values) + * Return: 0 if OK, 1 on error + */ +l_int32 +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 * + *---------------------------------------------------------------------------*/ +/*! + * pixConvertRGBToLAB() + * + * Input: pixs (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; +} + + +/*! + * fpixaConvertLABToRGB() + * + * Input: fpixa (three fpix: l,a,b) + * Return: pixd (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; +} + + +/*! + * convertRGBToLAB() + * + * Input: rval, gval, bval (rgb input) + * &flval, &faval, &fbval ( lab values) + * Return: 0 if OK, 1 on error + * + * Notes: + * (1) These conversions are for illuminant D65 acting on linear sRGB + * values. + */ +l_int32 +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; +} + + +/*! + * convertLABToRGB() + * + * Input: flval, faval, fbval + * &rval, &gval, &bval ( 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_int32 +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/liblept/src/compare.c b/liblept/src/compare.c index c51c607..11f0f37 100644 --- a/liblept/src/compare.c +++ b/liblept/src/compare.c @@ -274,7 +274,7 @@ PIXCMAP *cmap1, *cmap2; linebits = d1 * w1; fullwords = linebits / 32; endbits = linebits & 31; - endmask = 0xffffffff << (32 - endbits); + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); for (i = 0; i < h1; i++) { line1 = data1 + wpl1 * i; line2 = data2 + wpl2 * i; @@ -529,7 +529,8 @@ PIX *pixn; pixCountPixels(pix2, &count2, tab8); pixn = pixAnd(NULL, pix1, pix2); pixCountPixels(pixn, &countn, tab8); - *pval = (l_float32)(countn * countn) / (l_float32)(count1 * count2); + *pval = (l_float32)countn * (l_float32)countn / + ((l_float32)count1 * (l_float32)count2); FREE(tab8); return 0; } diff --git a/liblept/src/convolve.c b/liblept/src/convolve.c index a5e2399..830da4f 100644 --- a/liblept/src/convolve.c +++ b/liblept/src/convolve.c @@ -28,55 +28,58 @@ * convolve.c * * Top level grayscale or color block convolution - * PIX *pixBlockconv() + * PIX *pixBlockconv() * * Grayscale block convolution - * PIX *pixBlockconvGray() + * PIX *pixBlockconvGray() + * static void blockconvLow() * * Accumulator for 1, 8 and 32 bpp convolution - * PIX *pixBlockconvAccum() + * PIX *pixBlockconvAccum() + * static void blockconvAccumLow() * * Un-normalized grayscale block convolution - * PIX *pixBlockconvGrayUnnormalized() + * PIX *pixBlockconvGrayUnnormalized() * * Tiled grayscale or color block convolution - * PIX *pixBlockconvTiled() - * PIX *pixBlockconvGrayTile() + * 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() + * l_int32 pixWindowedStats() + * PIX *pixWindowedMean() + * PIX *pixWindowedMeanSquare() + * l_int32 pixWindowedVariance() + * DPIX *pixMeanSquareAccum() * * Binary block sum and rank filter - * PIX *pixBlockrank() - * PIX *pixBlocksum() + * PIX *pixBlockrank() + * PIX *pixBlocksum() + * static void blocksumLow() * * Census transform - * PIX *pixCensusTransform() + * PIX *pixCensusTransform() * * Generic convolution (with Pix) - * PIX *pixConvolve() - * PIX *pixConvolveSep() - * PIX *pixConvolveRGB() - * PIX *pixConvolveRGBSep() + * PIX *pixConvolve() + * PIX *pixConvolveSep() + * PIX *pixConvolveRGB() + * PIX *pixConvolveRGBSep() * * Generic convolution (with float arrays) - * FPIX *fpixConvolve() - * FPIX *fpixConvolveSep() + * FPIX *fpixConvolve() + * FPIX *fpixConvolveSep() * * Convolution with bias (for non-negative output) - * PIX *pixConvolveWithBias() + * PIX *pixConvolveWithBias() * * Set parameter for convolution subsampling - * void l_setConvolveSampling() + * void l_setConvolveSampling() * * Additive gaussian noise - * PIX *pixAddGaussNoise() - * l_float32 gaussDistribSampling() + * PIX *pixAddGaussNoise() + * l_float32 gaussDistribSampling() */ #include @@ -88,6 +91,17 @@ 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 * *----------------------------------------------------------------------*/ @@ -243,6 +257,173 @@ PIX *pixd, *pixt; } +/*! + * blockconvLow() + * + * Input: data (data of input image, to be convolved) + * w, h, wpl + * dataa (data of 32 bpp accumulator) + * wpla (accumulator) + * wc (convolution "half-width") + * 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. / (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 * *----------------------------------------------------------------------*/ @@ -288,6 +469,120 @@ PIX *pixd; } +/* + * blockconvAccumLow() + * + * Input: datad (32 bpp dest) + * w, h, wpld (of 32 bpp dest) + * datas (1, 8 or 32 bpp src) + * d (bpp of src) + * 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 * *----------------------------------------------------------------------*/ @@ -677,16 +972,16 @@ PIX *pixb, *pixm, *pixms; PROCNAME("pixWindowedStats"); - 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); 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) @@ -960,7 +1255,7 @@ FPIX *fpixv, *fpixrv; /* variance and square root of variance */ PROCNAME("pixWindowedVariance"); if (!pfpixv && !pfpixrv) - return ERROR_INT("&fpixv and &fpixrv not defined", procName, 1); + return ERROR_INT("no output requested", procName, 1); if (pfpixv) *pfpixv = NULL; if (pfpixrv) *pfpixrv = NULL; if (!pixm || pixGetDepth(pixm) != 8) @@ -1245,6 +1540,158 @@ PIX *pixt, *pixd; } +/*! + * blocksumLow() + * + * Input: datad (of 8 bpp dest) + * w, h, wpl (of 8 bpp dest) + * dataa (of 32 bpp accum) + * wpla (of 32 bpp accum) + * 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. / (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 * *----------------------------------------------------------------------*/ @@ -1859,6 +2306,9 @@ 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)) diff --git a/liblept/src/correlscore.c b/liblept/src/correlscore.c index 9dfe964..290a79b 100644 --- a/liblept/src/correlscore.c +++ b/liblept/src/correlscore.c @@ -142,9 +142,9 @@ l_uint32 *row1, *row2; return ERROR_INT("&score not defined", procName, 1); *pscore = 0.0; if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not 1 bpp", procName, 1); + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not 1 bpp", procName, 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) @@ -437,9 +437,9 @@ l_int32 threshold; PROCNAME("pixCorrelationScoreThresholded"); if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not 1 bpp", procName, 0); + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 0); if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not 1 bpp", procName, 0); + 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) @@ -741,9 +741,9 @@ PIX *pixt; return ERROR_INT("&score not defined", procName, 1); *pscore = 0.0; if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not 1 bpp", procName, 1); + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not 1 bpp", procName, 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) @@ -840,9 +840,9 @@ PIX *pixt; return ERROR_INT("&score not defined", procName, 1); *pscore = 0.0; if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not 1 bpp", procName, 1); + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not 1 bpp", procName, 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) diff --git a/liblept/src/dewarp1.c b/liblept/src/dewarp1.c index 8aefe55..2ed520a 100644 --- a/liblept/src/dewarp1.c +++ b/liblept/src/dewarp1.c @@ -389,24 +389,22 @@ static l_int32 dewarpaExtendArraysToSize(L_DEWARPA *dewa, l_int32 size); - - /* Special parameter values */ -static const l_int32 MIN_ARRAY_SAMPLING = 8; + /* Parameter values used in dewarpaCreate() */ +static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */ +static const l_int32 MAX_PTR_ARRAYSIZE = 10000; static const l_int32 DEFAULT_ARRAY_SAMPLING = 30; -static const l_int32 MIN_MIN_LINES = 4; +static const l_int32 MIN_ARRAY_SAMPLING = 8; static const l_int32 DEFAULT_MIN_LINES = 15; +static const l_int32 MIN_MIN_LINES = 4; static const l_int32 DEFAULT_MAX_REF_DIST = 16; -static const l_float32 DEFAULT_SLOPE_FACTOR = 2000.; - -static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */ -static const l_int32 MAX_PTR_ARRAYSIZE = 10000; + /* Parameter values used in dewarpaSetCurvatures() */ static const l_int32 DEFAULT_MAX_LINECURV = 180; static const l_int32 DEFAULT_MIN_DIFF_LINECURV = 0; -static const l_int32 DEFAULT_MAX_DIFF_LINECURV = 150; +static const l_int32 DEFAULT_MAX_DIFF_LINECURV = 200; static const l_int32 DEFAULT_MAX_EDGECURV = 50; -static const l_int32 DEFAULT_MAX_DIFF_EDGECURV = 30; -static const l_int32 DEFAULT_MAX_EDGESLOPE = 100; +static const l_int32 DEFAULT_MAX_DIFF_EDGECURV = 40; +static const l_int32 DEFAULT_MAX_EDGESLOPE = 80; /*----------------------------------------------------------------------* @@ -878,8 +876,11 @@ dewarpaGetDewarp(L_DEWARPA *dewa, if (!dewa) return (L_DEWARP *)ERROR_PTR("dewa not defined", procName, NULL); - if (index < 0 || index > dewa->maxpage) - return (L_DEWARP *)ERROR_PTR("invalid index", 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]; } diff --git a/liblept/src/dewarp2.c b/liblept/src/dewarp2.c index c709507..83f21f0 100644 --- a/liblept/src/dewarp2.c +++ b/liblept/src/dewarp2.c @@ -37,6 +37,8 @@ * static PTA *dewarpGetMeanVerticals() * PTAA *dewarpRemoveShortLines() * static l_int32 dewarpGetLineEndpoints() + * static l_int32 dewarpFindLongLines() + * static l_int32 dewarpIsLineCoverageValid() * static l_int32 dewarpQuadraticLSF() * * Build the line disparity model @@ -58,6 +60,8 @@ static l_int32 dewarpGetLineEndpoints(l_int32 h, PTAA *ptaa, PTA **pptal, PTA **pptar); static l_int32 dewarpFindLongLines(PTA *ptal, PTA *ptar, l_float32 minfract, PTA **pptald, PTA **pptard); +static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h, + l_int32 *ptopline, l_int32 *pbotline); 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); @@ -132,7 +136,7 @@ l_int32 dewarpBuildPageModel(L_DEWARP *dew, const char *debugfile) { -l_int32 ret; +l_int32 linecount, topline, botline, ret; PIX *pixs, *pix1, *pix2, *pix3; PTA *pta; PTAA *ptaa1, *ptaa2; @@ -187,9 +191,23 @@ PTAA *ptaa1, *ptaa2; ptaDestroy(&pta); } ptaaDestroy(&ptaa1); - if (ptaaGetCount(ptaa2) < dew->minlines) { + + /* 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 image foreground. */ + if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs), + &topline, &botline) == FALSE) { ptaaDestroy(&ptaa2); - L_WARNING("insufficient lines to build model\n", procName); + L_WARNING("invalid line coverage: [%d ... %d] in height %d\n", + procName, topline, botline, pixGetHeight(pixs)); return 1; } @@ -744,25 +762,39 @@ PTAA *ptaa; * 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(30, w / 30); + 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); - pixWrite("/tmp/dewmod/0011.tif", pix1, IFF_TIFF_G4); - pixDisplayWithTitle(pix1, 0, 800, "pix1", debugflag); - /* Get the 8-connected components ... */ - boxa = pixConnComp(pix1, &pixa1, 8); + /* 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) { + pixWrite("/tmp/dewmod/0011.tif", pix1, IFF_TIFF_G4); + pixDisplayWithTitle(pix1, 0, 600, "pix1", 1); + pixWrite("/tmp/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 and thin c.c */ + /* ... and remove the short width and very short height c.c */ pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH, - L_SELECT_IF_GT, 0); + L_SELECT_IF_GT, NULL); if ((nsegs = pixaGetCount(pixa2)) == 0) { pixaDestroy(&pixa1); pixaDestroy(&pixa2); @@ -770,8 +802,8 @@ PTAA *ptaa; } if (debugflag) { pix2 = pixaDisplay(pixa2, w, h); - pixWrite("/tmp/dewmod/0012.tif", pix2, IFF_TIFF_G4); - pixDisplayWithTitle(pix2, 800, 800, "pix2", 1); + pixWrite("/tmp/dewmod/0013.tif", pix2, IFF_TIFF_G4); + pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1); pixDestroy(&pix2); } @@ -789,8 +821,8 @@ PTAA *ptaa; if (debugflag) { pix1 = pixCreateTemplate(pixs); pix2 = pixDisplayPtaa(pix1, ptaa); - pixWrite("/tmp/dewmod/0013.tif", pix2, IFF_PNG); - pixDisplayWithTitle(pix2, 0, 1400, "pix3", 1); + pixWrite("/tmp/dewmod/0014.tif", pix2, IFF_PNG); + pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1); pixDestroy(&pix1); pixDestroy(&pix2); } @@ -983,65 +1015,6 @@ PTA *pta, *ptal, *ptar; } -/*! - * dewarpQuadraticLSF() - * - * Input: ptad (left or right end points of longest lines) - * &a ( coeff a of LSF: y = ax^2 + bx + c) - * &b ( coeff b of LSF: y = ax^2 + bx + c) - * &c ( coeff c of LSF: y = ax^2 + bx + c) - * &mederr ( 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; -} - - /*! * dewarpFindLongLines() * @@ -1169,6 +1142,117 @@ PTA *ptals, *ptars, *ptald, *ptard; } +/*! + * dewarpIsLineCoverageValid() + * + * Input: ptaa (of validated lines) + * h (height of pix) + * &topline ( location of top line) + * &botline ( 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 lines in both halves (top and bottom) + * of the image. + * (b) the coverage must be at least 40% of the image height + */ +static l_int32 +dewarpIsLineCoverageValid(PTAA *ptaa, + l_int32 h, + l_int32 *ptopline, + l_int32 *pbotline) +{ +l_int32 i, n, both_halves; +l_float32 top, bot, y, fraction; + + 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 (!ptopline || !pbotline) + return ERROR_INT("&topline and &botline not defined", procName, 0); + + top = 100000.0; + bot = 0.0; + for (i = 0; i < n; i++) { + ptaaGetPt(ptaa, i, 0, NULL, &y); + if (y < top) top = y; + if (y > bot) bot = y; + } + *ptopline = (l_int32)top; + *pbotline = (l_int32)bot; + both_halves = top < 0.5 * h && bot > 0.5 * h; + fraction = (bot - top) / h; + if (both_halves && fraction > 0.40) + return 1; + return 0; +} + + +/*! + * dewarpQuadraticLSF() + * + * Input: ptad (left or right end points of longest lines) + * &a ( coeff a of LSF: y = ax^2 + bx + c) + * &b ( coeff b of LSF: y = ax^2 + bx + c) + * &c ( coeff c of LSF: y = ax^2 + bx + c) + * &mederr ( 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 line disparity model * *----------------------------------------------------------------------*/ diff --git a/liblept/src/dewarp3.c b/liblept/src/dewarp3.c index 0002b86..dd54bcd 100644 --- a/liblept/src/dewarp3.c +++ b/liblept/src/dewarp3.c @@ -29,10 +29,15 @@ * * Applying and stripping the page disparity model * - * Apply disparity array + * Apply disparity array to pix * l_int32 dewarpaApplyDisparity() - * static l_int32 pixApplyVertDisparity() - * static l_int32 pixApplyHorizDisparity() + * 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() @@ -47,12 +52,18 @@ #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 * + * Apply warping disparity array to pixa * *----------------------------------------------------------------------*/ /*! * dewarpaApplyDisparity() @@ -105,8 +116,7 @@ dewarpaApplyDisparity(L_DEWARPA *dewa, PIX **ppixd, const char *debugfile) { -l_int32 debug; -L_DEWARP *dew1, *dew2; +L_DEWARP *dew1, *dew; PIX *pixv, *pixh; PROCNAME("dewarpaApplyDisparity"); @@ -116,49 +126,18 @@ PIX *pixv, *pixh; if (!ppixd) return ERROR_INT("&pixd not defined", procName, 1); *ppixd = pixClone(pixs); - - 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 (grayin > 255) - return ERROR_INT("invalid grayin", procName, 1); - if (x < 0) x = 0; - if (y < 0) y = 0; - - /* Make sure all models are valid and all refmodels have - * been added to dewa */ - debug = (debugfile) ? 1 : 0; - 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("dew1 not defined; no valid model for page %d\n", - procName, pageno); - return 1; + if (grayin > 255) { + L_WARNING("invalid grayin = %d; clipping at 255\n", procName, grayin); + grayin = 255; } - /* 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); - - /* 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); + /* 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(dew2, pixs, grayin)) == NULL) { - dewarpMinimize(dew2); + if ((pixv = pixApplyVertDisparity(dew, pixs, grayin)) == NULL) { + dewarpMinimize(dew); return ERROR_INT("pixv not made", procName, 1); } pixDestroy(ppixd); @@ -172,11 +151,11 @@ PIX *pixv, *pixh; } /* Optionally, correct for horizontal disparity */ - if (dewa->useboth && dew2->hsuccess) { - if (dew2->hvalid == FALSE) { + if (dewa->useboth && dew->hsuccess) { + if (dew->hvalid == FALSE) { L_INFO("invalid horiz model for page %d\n", procName, pageno); } else { - if ((pixh = pixApplyHorizDisparity(dew2, pixv, grayin)) != NULL) { + if ((pixh = pixApplyHorizDisparity(dew, pixv, grayin)) != NULL) { pixDestroy(ppixd); *ppixd = pixh; if (debugfile) { @@ -191,6 +170,7 @@ PIX *pixv, *pixh; } if (debugfile) { + dew1 = dewarpaGetDewarp(dewa, pageno); dewarpDebug(dew1, "dewapply", 0); convertFilesToPdf("/tmp/dewapply", NULL, 135, 1.0, 0, 0, "Dewarp Apply Disparity", debugfile); @@ -198,12 +178,88 @@ PIX *pixv, *pixh; } /* Get rid of the large full res disparity arrays */ - dewarpMinimize(dew2); + dewarpMinimize(dew); return 0; } +/*! + * dewarpaApplyInit() + * + * Input: dewa + * pageno (of page model to be used; may be a ref model) + * pixs (image to be modified; can be 1, 8 or 32 bpp) + * x, y (origin for generation of disparity arrays) + * &pdew ( dewarp to be used for this page + * 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) See dewarpApplyDisparity() for other details on inputs. + */ +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 debug; +L_DEWARP *dew1, *dew2; + + 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; + + /* 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; +} + + /*! * pixApplyVertDisparity() * @@ -417,6 +473,211 @@ PIX *pixd; } +/*----------------------------------------------------------------------* + * Apply warping disparity array to boxa * + *----------------------------------------------------------------------*/ +/*! + * dewarpaApplyDisparityBoxa() + * + * Input: dewa + * pageno (of page model to be used; may be a ref model) + * pixs (initial pix reference; for alignment and debugging) + * boxas (boxa to be mapped) + * mapdir (1 if mapping forward from original to dewarped; + * 0 if backward) + * x, y (origin for generation of disparity arrays with + * respect to the source region) + * &boxad ( disparity corrected boxa) + * 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_int32 +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("dewboxa"); /* remove previous images */ + lept_mkdir("dewboxa"); + pix1 = pixConvertTo32(pixs); + pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0); + pixWrite("/tmp/dewboxa/01.png", pix1, IFF_PNG); + pixDestroy(&pix1); + pixv = pixApplyVertDisparity(dew, pixs, 255); + pix1 = pixConvertTo32(pixv); + pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0); + pixWrite("/tmp/dewboxa/02.png", pix1, IFF_PNG); + pixDestroy(&pix1); + } + + /* Optionally, correct for horizontal disparity */ + if (dewa->useboth && dew->hsuccess) { + 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); + pixWrite("/tmp/dewboxa/03.png", pix1, IFF_PNG); + pixDestroy(&pixh); + pixDestroy(&pix1); + } + } + } + } + + if (debug_out) { + pixDestroy(&pixv); + dew1 = dewarpaGetDewarp(dewa, pageno); + dewarpDebug(dew1, "dewapply", 0); + convertFilesToPdf("/tmp/dewboxa", NULL, 135, 1.0, 0, 0, + "Dewarp Apply Disparity Boxa", debugfile); + fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file made: %s\n", + debugfile); + } + + /* Get rid of the large full res disparity arrays */ + dewarpMinimize(dew); + + return 0; +} + + +/*! + * boxaApplyDisparity() + * + * Input: dew + * boxa + * direction (L_HORIZ or L_VERT) + * 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 * *----------------------------------------------------------------------*/ diff --git a/liblept/src/dewarp4.c b/liblept/src/dewarp4.c index ddf2028..c111a78 100644 --- a/liblept/src/dewarp4.c +++ b/liblept/src/dewarp4.c @@ -33,6 +33,8 @@ * * Top-level single page dewarper * l_int32 dewarpSinglePage() + * l_int32 dewarpSinglePageInit() + * l_int32 dewarpSinglePageRun() * * Operations on dewarpa * l_int32 dewarpaListPages() @@ -56,9 +58,13 @@ 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 debuging */ +#endif /* !NO_CONSOLE_IO */ + /* Special parameter values */ static const l_int32 GRAYIN_VALUE = 200; - + /*----------------------------------------------------------------------* * Top-level single page dewarper * @@ -90,11 +96,8 @@ dewarpSinglePage(PIX *pixs, L_DEWARPA **pdewa, l_int32 debug) { -const char *debugfile; -l_int32 vsuccess, ret; -L_DEWARP *dew; -L_DEWARPA *dewa; -PIX *pix1, *pixb; +L_DEWARPA *dewa; +PIX *pixb; PROCNAME("dewarpSinglePage"); @@ -105,20 +108,121 @@ PIX *pix1, *pixb; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); - dewa = dewarpaCreate(1, 0, 1, 0, -1); - dewarpaUseBothArrays(dewa, use_both); + dewarpSinglePageInit(pixs, thresh, adaptive, use_both, &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; +} + + +/*! + * dewarpSinglePageInit() + * + * Input: pixs (with text, any depth) + * thresh (for global thresholding to 1 bpp; ignored otherwise) + * adaptive (1 for adaptive thresholding; 0 for global threshold) + * use_both (1 for horizontal and vertical; 0 for vertical only) + * &pixb ( 1 bpp image) + * &dewa ( 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, &pixb, &dewa); + * dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150); + * dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0); + * dewarpaDestroy(&dewa); + * pixDestroy(&pixb); + */ +l_int32 +dewarpSinglePageInit(PIX *pixs, + l_int32 thresh, + l_int32 adaptive, + l_int32 use_both, + 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, use_both); /* Generate a binary image, if necessary */ if (pixGetDepth(pixs) > 1) { pix1 = pixConvertTo8(pixs, 0); if (adaptive) - pixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0); + *ppixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0); else - pixb = pixThresholdToBinary(pix1, thresh); + *ppixb = pixThresholdToBinary(pix1, thresh); pixDestroy(&pix1); } else { - pixb = pixClone(pixs); + *ppixb = pixClone(pixs); } + return 0; +} + + +/*! + * dewarpSinglePageRun() + * + * Input: pixs (any depth) + * pixb (1 bpp) + * dewa (initialized) + * &pixd ( dewarped result) + * 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 model parameters must be set before calling this. + * (3) If a model cannot be built, this returns a copy of pixs in &pixd. + */ +l_int32 +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("pixs not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); /* Generate the page model */ lept_mkdir("lept"); @@ -128,9 +232,7 @@ PIX *pix1, *pixb; dewarpBuildPageModel(dew, debugfile); dewarpaModelStatus(dewa, 0, &vsuccess, NULL); if (vsuccess == 0) { - L_ERROR("failure to build model\n", procName); - pixDestroy(&pixb); - dewarpaDestroy(&dewa); + L_ERROR("failure to build model for vertical disparity\n", procName); *ppixd = pixCopy(NULL, pixs); return 0; } @@ -140,11 +242,6 @@ PIX *pix1, *pixb; ret = dewarpaApplyDisparity(dewa, 0, pixs, 255, 0, 0, ppixd, debugfile); if (ret) L_ERROR("invalid model; failure to apply disparity\n", procName); - if (pdewa) - *pdewa = dewa; - else - dewarpaDestroy(&dewa); - pixDestroy(&pixb); return 0; } @@ -575,6 +672,16 @@ L_DEWARP *dew; 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; } @@ -713,7 +820,13 @@ l_int32 maxcurv, diffcurv, diffedge; diffcurv <= dewa->max_diff_linecurv) { dew->vvalid = 1; } else { - L_INFO("invalid vert model for page %d\n", procName, dew->pageno); + 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. */ @@ -726,7 +839,19 @@ l_int32 maxcurv, diffcurv, diffedge; diffedge <= dewa->max_diff_edgecurv) { dew->hvalid = 1; } else { - L_INFO("invalid horiz model for page %d\n", procName, dew->pageno); + 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 */ } } @@ -880,6 +1005,8 @@ PIX *pixv, *pixh; 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)); diff --git a/liblept/src/dnabasic.c b/liblept/src/dnabasic.c index 396cbe4..125c041 100644 --- a/liblept/src/dnabasic.c +++ b/liblept/src/dnabasic.c @@ -879,6 +879,10 @@ l_dnaGetParameters(L_DNA *da, { 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); diff --git a/liblept/src/encoding.c b/liblept/src/encoding.c new file mode 100644 index 0000000..7dd8413 --- /dev/null +++ b/liblept/src/encoding.c @@ -0,0 +1,638 @@ +/*====================================================================* + - 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(l_uint8 *inarray, l_int32 insize, + l_int32 *pindex, char *outbuf, + l_int32 *pnbout); + + +/*-------------------------------------------------------------* + * Utility for encoding and decoding data with base64 * + *-------------------------------------------------------------*/ +/*! + * encodeBase64() + * + * Input: inarray (input binary data) + * insize (number of bytes in input array) + * &outsize ( 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 ouput encoded data consists of the 64 characters + * in the base64 set, plus newlines and the pad character '='. + */ +char * +encodeBase64(l_uint8 *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char *chara; +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 *)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; +} + + +/*! + * decodeBase64() + * + * Input: inarray (input encoded char data, with 72 chars/line)) + * insize (number of bytes in input array) + * &outsize ( 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 *)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; + + FREE(rtable64); + return bytea; +} + + +/*! + * isBase64() + */ +static l_int32 +isBase64(char c) +{ + return (isalnum(((int)c)) || ((c) == '+') || ((c) == '/')) ? 1 : 0; +} + +/*! + * genReverseTab64() + */ +static l_int32 * +genReverseTab64() +{ +l_int32 i; +l_int32 *rtable64; + + rtable64 = (l_int32 *)CALLOC(128, sizeof(l_int32)); + for (i = 0; i < 64; i++) { + rtable64[(unsigned char)tablechar64[i]] = i; + } + return rtable64; +} + +/*! + * 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; +} + +/*! + * 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 * + *-------------------------------------------------------------*/ +/*! + * encodeAscii85() + * + * Input: inarray (input data) + * insize (number of bytes in input array) + * &outsize ( 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(l_uint8 *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char *chara; +char *outbuf; +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 *)CALLOC(maxsize, sizeof(char))) == NULL) + return (char *)ERROR_PTR("chara not made", procName, NULL); + if ((outbuf = (char *)CALLOC(8, sizeof(char))) == NULL) + return (char *)ERROR_PTR("outbuf 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; + } + } + + FREE(outbuf); + *poutsize = outindex; + return chara; +} + + +/*! + * convertChunkToAscii85() + * + * Input: inarray (input data) + * insize (number of bytes in input array) + * &index (use and -- ptr) + * outbuf (holds 8 ascii chars; we use no more than 7) + * &nbsout ( 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(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; +} + + +/*! + * decodeAscii85() + * + * Input: inarray (ascii85 input data) + * insize (number of bytes in input array) + * &outsize ( 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(char *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char inc; +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 *)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 * + *-------------------------------------------------------------*/ +/*! + * reformatPacked64() + * + * Input: inarray (base64 encoded string with newlines) + * insize (number of bytes in input array) + * leadspace (number of spaces in each line before the data) + * linechars (number of bytes of data in each line; multiple of 4) + * addquotes (1 to add quotes to each line of data; 0 to skip) + * &outsize ( 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(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 *)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 *)CALLOC(nlines * linewithpad, sizeof(char))) == NULL) + 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; + + FREE(flata); + return outa; +} + diff --git a/liblept/src/enhance.c b/liblept/src/enhance.c index 34d08a3..383d132 100644 --- a/liblept/src/enhance.c +++ b/liblept/src/enhance.c @@ -605,7 +605,7 @@ PIX * pixEqualizeTRC(PIX *pixd, PIX *pixs, l_float32 fract, - l_int32 factor) + l_int32 factor) { l_int32 d; NUMA *na; @@ -688,8 +688,8 @@ PIXCMAP *cmap; */ NUMA * numaEqualizeTRC(PIX *pix, - l_float32 fract, - l_int32 factor) + l_float32 fract, + l_int32 factor) { l_int32 iin, iout, itarg; l_float32 val, sum; diff --git a/liblept/src/fhmtauto.c b/liblept/src/fhmtauto.c index 610d6c1..b5e69fa 100644 --- a/liblept/src/fhmtauto.c +++ b/liblept/src/fhmtauto.c @@ -73,11 +73,11 @@ * (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, char *selname); + * PIX *pixHMTDwa_1(PIX *pixd, PIX *pixs, const char *selname); * * or * - * PIX *pixFHMTGen_1(PIX *pixd, PIX *pixs, char *selname); + * 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 @@ -95,10 +95,10 @@ #define TEMPLATE1 "hmttemplate1.txt" #define TEMPLATE2 "hmttemplate2.txt" -#define BUFFER_SIZE 512 - #define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);" +static const l_int32 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); @@ -249,7 +249,7 @@ 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[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; l_int32 i, nsels, nbytes, actstart, end, newstart; size_t size; SARRAY *sa1, *sa2, *sa3; @@ -275,10 +275,10 @@ SARRAY *sa1, *sa2, *sa3; /* Make strings containing function call names */ sprintf(bigbuf, "PIX *pixHMTDwa_%d(PIX *pixd, PIX *pixs, " - "char *selname);", fileindex); + "const char *selname);", fileindex); str_proto1 = stringNew(bigbuf); sprintf(bigbuf, "PIX *pixFHMTGen_%d(PIX *pixd, PIX *pixs, " - "char *selname);", fileindex); + "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" @@ -293,9 +293,9 @@ SARRAY *sa1, *sa2, *sa3; str_doc3 = stringNew(bigbuf); sprintf(bigbuf, " * pixFHMTGen_%d()", fileindex); str_doc4 = stringNew(bigbuf); - sprintf(bigbuf, "pixHMTDwa_%d(PIX *pixd,", fileindex); + sprintf(bigbuf, "pixHMTDwa_%d(PIX *pixd,", fileindex); str_def1 = stringNew(bigbuf); - sprintf(bigbuf, "pixFHMTGen_%d(PIX *pixd,", fileindex); + sprintf(bigbuf, "pixFHMTGen_%d(PIX *pixd,", fileindex); str_def2 = stringNew(bigbuf); sprintf(bigbuf, " PROCNAME(\"pixHMTDwa_%d\");", fileindex); str_proc1 = stringNew(bigbuf); @@ -305,11 +305,11 @@ SARRAY *sa1, *sa2, *sa3; fileindex); str_dwa1 = stringNew(bigbuf); sprintf(bigbuf, - " fhmtgen_low_%d(datad, w, h, wpld, datat, wpls, index);", + " 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);", + " fhmtgen_low_%d(datad, w, h, wpld, datas, wpls, index);", fileindex); str_low_ds = stringNew(bigbuf); @@ -355,7 +355,7 @@ SARRAY *sa1, *sa2, *sa3; sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); - /* Finish pixMorphDwa_*() function definition */ + /* Finish pixHMTDwa_*() function definition */ sarrayAddString(sa3, str_def1, L_INSERT); sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); @@ -427,7 +427,7 @@ fhmtautogen2(SELA *sela, { char *filestr, *fname, *linestr; char *str_doc1, *str_doc2, *str_doc3, *str_def1; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; char breakstring[] = " break;"; char staticstring[] = "static void"; l_int32 i, k, l, nsels, nbytes, nhits, nmisses; @@ -625,7 +625,7 @@ SARRAY *sa; ymax = 0; for (i = 0; i < sel->sy; i++) { for (j = 0; j < sel->sx; j++) { - if (sel->data[i][j] == 1) { + if (sel->data[i][j] == 1 || sel->data[i][j] == 2) { dely = L_ABS(i - sel->cy); ymax = L_MAX(ymax, dely); } @@ -677,7 +677,7 @@ sarrayMakeInnerLoopDWACode(SEL *sel, { char *string; char land[] = "&"; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; l_int32 i, j, ntot, nfound, type, delx, dely; SARRAY *sa; @@ -730,7 +730,7 @@ makeBarrelshiftString(l_int32 delx, /* j - cx */ l_int32 type) /* SEL_HIT or SEL_MISS */ { l_int32 absx, absy; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; PROCNAME("makeBarrelshiftString"); diff --git a/liblept/src/fhmtgen.1.c b/liblept/src/fhmtgen.1.c index af9ca5f..2d0231d 100644 --- a/liblept/src/fhmtgen.1.c +++ b/liblept/src/fhmtgen.1.c @@ -34,8 +34,8 @@ #include #include "allheaders.h" -PIX *pixHMTDwa_1(PIX *pixd, PIX *pixs, char *selname); -PIX *pixFHMTGen_1(PIX *pixd, PIX *pixs, char *selname); +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, @@ -68,9 +68,9 @@ static char SEL_NAMES[][80] = { * See notes below for that function. */ PIX * -pixHMTDwa_1(PIX *pixd, - PIX *pixs, - char *selname) +pixHMTDwa_1(PIX *pixd, + PIX *pixs, + const char *selname) { PIX *pixt1, *pixt2, *pixt3; @@ -114,9 +114,9 @@ PIX *pixt1, *pixt2, *pixt3; * before erosion and dilation. */ PIX * -pixFHMTGen_1(PIX *pixd, - PIX *pixs, - char *selname) +pixFHMTGen_1(PIX *pixd, + PIX *pixs, + const char *selname) { l_int32 i, index, found, w, h, wpls, wpld; l_uint32 *datad, *datas, *datat; diff --git a/liblept/src/fmorphauto.c b/liblept/src/fmorphauto.c index b5cadc2..bf023ea 100644 --- a/liblept/src/fmorphauto.c +++ b/liblept/src/fmorphauto.c @@ -99,10 +99,10 @@ #define TEMPLATE1 "morphtemplate1.txt" #define TEMPLATE2 "morphtemplate2.txt" -#define BUFFER_SIZE 512 - #define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);" +static const l_int32 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); @@ -284,7 +284,7 @@ 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[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; l_int32 i, nsels, nbytes, actstart, end, newstart; size_t size; SARRAY *sa1, *sa2, *sa3; @@ -339,7 +339,7 @@ SARRAY *sa1, *sa2, *sa3; str_proc2 = stringNew(bigbuf); sprintf(bigbuf, " pixt2 = pixFMorphopGen_%d(NULL, pixt1, operation, selname);", - fileindex); + fileindex); str_dwa1 = stringNew(bigbuf); sprintf(bigbuf, " fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index);", @@ -489,7 +489,7 @@ fmorphautogen2(SELA *sela, { char *filestr, *linestr, *fname; char *str_doc1, *str_doc2, *str_doc3, *str_doc4, *str_def1; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; char breakstring[] = " break;"; char staticstring[] = "static void"; l_int32 i, nsels, nbytes, actstart, end, newstart; @@ -605,7 +605,7 @@ SEL *sel; /* Do all the static functions */ for (i = 0; i < 2 * nsels; i++) { - /* Generate the function header and add the common args */ + /* 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); @@ -620,7 +620,7 @@ SEL *sel; sarrayConcatenate(sa4, sa5); sarrayDestroy(&sa5); - /* Add the function loop code */ + /* Add the function loop code */ sarrayAppendRange(sa4, sa1, loopstart, loopend); /* Insert barrel-op code for *dptr */ @@ -629,7 +629,7 @@ SEL *sel; sarrayConcatenate(sa4, sa6); sarrayDestroy(&sa6); - /* Finish the function code */ + /* Finish the function code */ sarrayAppendRange(sa4, sa1, finalstart, finalend); } @@ -694,7 +694,7 @@ SARRAY *sa; for (i = 0; i < ymax; i++) { if (vshift[i] == 0) { allvshifts = FALSE; - break; + break; } } @@ -748,7 +748,7 @@ sarrayMakeInnerLoopDWACode(SEL *sel, char *tstr, *string; char logicalor[] = "|"; char logicaland[] = "&"; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; l_int32 i, j, optype, count, nfound, delx, dely; SARRAY *sa; @@ -822,7 +822,7 @@ makeBarrelshiftString(l_int32 delx, /* j - cx */ l_int32 dely) /* i - cy */ { l_int32 absx, absy; -char bigbuf[BUFFER_SIZE]; +char bigbuf[L_BUF_SIZE]; PROCNAME("makeBarrelshiftString"); diff --git a/liblept/src/fpix1.c b/liblept/src/fpix1.c index c607a4d..c6b4d0c 100644 --- a/liblept/src/fpix1.c +++ b/liblept/src/fpix1.c @@ -68,6 +68,7 @@ * l_int32 fpixaChangeRefcount() * FPIX *fpixaGetFPix() * l_int32 fpixaGetFPixDimensions() + * l_float32 *fpixaGetData() * l_int32 fpixaGetPixel() * l_int32 fpixaSetPixel() * @@ -391,6 +392,10 @@ fpixGetDimensions(FPIX *fpix, { 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; @@ -912,6 +917,10 @@ 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) @@ -925,6 +934,36 @@ FPIX *fpix; } +/*! + * fpixaGetData() + * + * Input: fpixa + * 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; +} + + /*! * fpixaGetPixel() * @@ -1259,6 +1298,10 @@ dpixGetDimensions(DPIX *dpix, { 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; diff --git a/liblept/src/fpix2.c b/liblept/src/fpix2.c index 709e8d7..172e5bd 100644 --- a/liblept/src/fpix2.c +++ b/liblept/src/fpix2.c @@ -675,7 +675,7 @@ l_float32 minval; PROCNAME("fpixGetMin"); if (!pminval && !pxminloc && !pyminloc) - return ERROR_INT("nothing to do", procName, 1); + return ERROR_INT("no return val requested", procName, 1); if (pminval) *pminval = 0.0; if (pxminloc) *pxminloc = 0; if (pyminloc) *pyminloc = 0; @@ -728,7 +728,7 @@ l_float32 maxval; PROCNAME("fpixGetMax"); if (!pmaxval && !pxmaxloc && !pymaxloc) - return ERROR_INT("nothing to do", procName, 1); + return ERROR_INT("no return val requested", procName, 1); if (pmaxval) *pmaxval = 0.0; if (pxmaxloc) *pxmaxloc = 0; if (pymaxloc) *pymaxloc = 0; @@ -781,7 +781,7 @@ l_float64 minval; PROCNAME("dpixGetMin"); if (!pminval && !pxminloc && !pyminloc) - return ERROR_INT("nothing to do", procName, 1); + return ERROR_INT("no return val requested", procName, 1); if (pminval) *pminval = 0.0; if (pxminloc) *pxminloc = 0; if (pyminloc) *pyminloc = 0; @@ -834,7 +834,7 @@ l_float64 maxval; PROCNAME("dpixGetMax"); if (!pmaxval && !pxmaxloc && !pymaxloc) - return ERROR_INT("nothing to do", procName, 1); + return ERROR_INT("no return val requested", procName, 1); if (pmaxval) *pmaxval = 0.0; if (pxmaxloc) *pxmaxloc = 0; if (pymaxloc) *pymaxloc = 0; diff --git a/liblept/src/gifio.c b/liblept/src/gifio.c index bf6fe20..00525eb 100644 --- a/liblept/src/gifio.c +++ b/liblept/src/gifio.c @@ -116,12 +116,18 @@ PIX *pixd, *pixdi; PIXCMAP *cmap; ColorMapObject *gif_cmap; SavedImage si; +#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR > 0 int giferr; +#endif PROCNAME("pixReadStreamGif"); if ((fd = fileno(fp)) < 0) return (PIX *)ERROR_PTR("invalid file descriptor", procName, NULL); +#ifdef _WIN32 + fd = _dup(fd); +#endif /* _WIN32 */ + #ifndef _MSC_VER lseek(fd, 0, SEEK_SET); #else @@ -177,8 +183,10 @@ int giferr; d = 4; else d = 8; - if ((cmap = pixcmapCreate(d)) == NULL) + 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; @@ -287,7 +295,9 @@ PIXCMAP *cmap; GifFileType *gif; ColorMapObject *gif_cmap; GifByteType *gif_line; +#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR > 0 int giferr; +#endif PROCNAME("pixWriteStreamGif"); @@ -299,6 +309,9 @@ int giferr; if ((fd = fileno(fp)) < 0) return ERROR_INT("invalid file descriptor", procName, 1); +#ifdef _WIN32 + fd = _dup(fd); +#endif /* _WIN32 */ d = pixGetDepth(pix); if (d == 32) { diff --git a/liblept/src/gplot.c b/liblept/src/gplot.c index d1116af..276d583 100644 --- a/liblept/src/gplot.c +++ b/liblept/src/gplot.c @@ -98,8 +98,7 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; const char *gplotstylenames[] = {"with lines", "with points", diff --git a/liblept/src/graymorph.c b/liblept/src/graymorph.c index c82c61f..5520b21 100644 --- a/liblept/src/graymorph.c +++ b/liblept/src/graymorph.c @@ -28,17 +28,25 @@ /* * graymorph.c * - * Top-level binary morphological operations (van Herk / Gil-Werman) - * PIX *pixErodeGray() - * PIX *pixDilateGray() - * PIX *pixOpenGray() - * PIX *pixCloseGray() + * 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() - * PIX *pixDilateGray3() - * PIX *pixOpenGray3() - * PIX *pixCloseGray3() + * 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 @@ -53,18 +61,82 @@ * 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 gray morphological operations * + * Top-level grayscale morphological operations * *-----------------------------------------------------------------*/ /*! * pixErodeGray() @@ -1020,3 +1092,219 @@ PIX *pixt, *pixb, *pixbd, *pixd; pixDestroy(&pixbd); return pixd; } + + +/*-----------------------------------------------------------------* + * Low-level gray morphological operations * + *-----------------------------------------------------------------*/ +/*! + * dilateGrayLow() + * + * Input: datad, w, h, wpld (8 bpp image) + * datas, wpls (8 bpp image, of same dimensions) + * size (full length of SEL; restricted to odd numbers) + * direction (L_HORIZ or L_VERT) + * buffer (holds full line or column of src image pixels) + * 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; +} + + +/*! + * erodeGrayLow() + * + * Input: datad, w, h, wpld (8 bpp image) + * datas, wpls (8 bpp image, of same dimensions) + * size (full length of SEL; restricted to odd numbers) + * direction (L_HORIZ or L_VERT) + * buffer (holds full line or column of src image pixels) + * 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/liblept/src/hmttemplate1.txt b/liblept/src/hmttemplate1.txt index 5f221bb..96a473d 100644 --- a/liblept/src/hmttemplate1.txt +++ b/liblept/src/hmttemplate1.txt @@ -62,8 +62,8 @@ */ PIX * --- pixHMTDwa_*(PIX *pixd, - PIX *pixs, - char *selname) + PIX *pixs, + const char *selname) { PIX *pixt1, *pixt2, *pixt3; @@ -108,8 +108,8 @@ PIX *pixt1, *pixt2, *pixt3; */ PIX * --- pixFHMTGen_*(PIX *pixd, - PIX *pixs, - char *selname) + PIX *pixs, + const char *selname) { l_int32 i, index, found, w, h, wpls, wpld; l_uint32 *datad, *datas, *datat; diff --git a/liblept/src/jbclass.c b/liblept/src/jbclass.c index 38f98f8..b55022d 100644 --- a/liblept/src/jbclass.c +++ b/liblept/src/jbclass.c @@ -206,8 +206,7 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; /* For jbClassifyRankHaus(): size of border added around * pix of each c.c., to allow further processing. This @@ -233,7 +232,7 @@ 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 */ /* Max allowed dilation to merge characters into words */ -#define MAX_ALLOWED_DILATION 25 +static const l_int32 MAX_ALLOWED_DILATION = 25; /* This stores the state of a state machine which fetches * similar sized templates */ diff --git a/liblept/src/jp2kheader.c b/liblept/src/jp2kheader.c index fa5e723..e1a3773 100644 --- a/liblept/src/jp2kheader.c +++ b/liblept/src/jp2kheader.c @@ -44,6 +44,10 @@ #include #include "allheaders.h" +#ifndef NO_CONSOLE_IO +#define DEBUG_IHDR 0 +#endif /* ~NO_CONSOLE_IO */ + /* --------------------------------------------*/ #if USE_JP2KHEADER /* defined in environ.h */ /* --------------------------------------------*/ @@ -184,8 +188,10 @@ l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* '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; val = *((l_uint32 *)data + windex); @@ -210,7 +216,7 @@ l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* 'ihdr' */ * * Input: stream (opened for read) * &xres, &yres ( resolution in ppi) - * Return: 0 if OK; 1 on error + * 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; @@ -257,7 +263,7 @@ l_float64 xres, yres; if (!found) { L_WARNING("image resolution not found\n", procName); FREE(data); - return 0; + return 1; } /* Extract the fields and calculate the resolution in pixels/meter. diff --git a/liblept/src/jp2kio.c b/liblept/src/jp2kio.c index 53a6db0..9eb88d1 100644 --- a/liblept/src/jp2kio.c +++ b/liblept/src/jp2kio.c @@ -65,23 +65,25 @@ * strings. * * N.B. - * * This is based on openjpeg-2.0 or 2.1. + * * 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.X. - * * For version 2.X, the openjpeg.h file is installed in an - * openjpeg-2.X subdirectory, which is hard to support. + * 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 both 2.0 and 2.1. - * The high-level interface to openjpeg continues to change. In 2.1, the - * ability to interface to a C file stream has been removed permanently. - * Leptonica requires either a file stream or memory buffer interface - * to each compression library. 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(). + * * 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 @@ -106,17 +108,13 @@ #if HAVE_LIBJP2K /* defined in environ.h */ /* --------------------------------------------*/ - /* Leptonica supports both 2.0 and 2.1. If you have 2.0, - * change MINOR to 0. */ -#define MINOR 1 + /* Leptonica supports both 2.0 and 2.1. */ +#include LIBJP2K_HEADER -#if MINOR == 0 - static const l_int32 OpjMinor = 0; - #include "openjpeg-2.0/openjpeg.h" -#else - static const l_int32 OpjMinor = 1; - #include "openjpeg-2.1/openjpeg.h" -#endif /* MINOR == 0 */ + /* 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 @@ -127,12 +125,27 @@ 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 raster data stored in their imaage. */ + * because the codec works on data stored in their raster format. */ static opj_image_t *pixConvertToOpjImage(PIX *pix); -#ifndef NO_CONSOLE_IO -#define DEBUG_INFO 0 -#endif /* ~NO_CONSOLE_IO */ +/*---------------------------------------------------------------------* + * 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) * @@ -144,6 +157,7 @@ static opj_image_t *pixConvertToOpjImage(PIX *pix); * reduction (scaling factor: 1, 2, 4, 8, 16) * box ( for extracting a subregion), can be null * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * debug (output callback messages, etc) * Return: pix (8 or 32 bpp), or null on error * * Notes: @@ -163,22 +177,23 @@ static opj_image_t *pixConvertToOpjImage(PIX *pix); * (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): - * 1 ==> grayscale [8 bpp pix] - * 2 ==> grascale + alpha [32 bpp pix] - * 3 ==> rgb [32 bpp pix] - * 4 ==> rgba [32 bpp pix] - * (6) The @hint parameter is not yet in use. + * (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 hint, + l_int32 debug) { -l_int32 ret; FILE *fp; PIX *pix; @@ -189,7 +204,7 @@ PIX *pix; if ((fp = fopenReadStream(filename)) == NULL) return (PIX *)ERROR_PTR("image file not found", procName, NULL); - pix = pixReadStreamJp2k(fp, reduction, box, hint); + pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); fclose(fp); if (!pix) @@ -205,6 +220,7 @@ PIX *pix; * reduction (scaling factor: 1, 2, 4, 8) * box ( for extracting a subregion), can be null * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * debug (output callback messages, etc) * Return: pix (8 or 32 bpp), or null on error * * Notes: @@ -214,11 +230,12 @@ PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box, - l_int32 hint) + 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, colorspace, prec; +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 */ @@ -237,11 +254,11 @@ PIX *pix = NULL; L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); return NULL; } - if ((opjVersion[2] - 0x30) != OpjMinor) { + if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { L_ERROR("version %s: differs from minor = %d\n", - procName, opjVersion, OpjMinor); - return NULL; - } + procName, opjVersion, OPJ_VERSION_MINOR); + return NULL; + } /* Get the resolution and the bits/sample */ rewind(fp); @@ -283,6 +300,13 @@ PIX *pix = NULL; 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); @@ -292,7 +316,7 @@ PIX *pix = NULL; } /* Read the main header of the codestream and, if necessary, - * the JP2 boxes*/ + * 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); @@ -334,16 +358,17 @@ PIX *pix = NULL; prec = image->comps[0].prec; if (prec != bps) L_WARNING("precision %d != bps %d!\n", procName, prec, bps); -#if 1 - 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); -#endif + 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); + } /* Free the codec structure */ if (l_codec) @@ -415,6 +440,7 @@ PIX *pix = NULL; * quality (SNR > 0; default ~34; 0 for lossless encoding) * nlevels (resolution levels; <= 10; default = 5) * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * debug (output callback messages, etc) * Return: 0 if OK; 1 on error * * Notes: @@ -437,7 +463,8 @@ pixWriteJp2k(const char *filename, PIX *pix, l_int32 quality, l_int32 nlevels, - l_int32 hint) + l_int32 hint, + l_int32 debug) { FILE *fp; @@ -451,7 +478,7 @@ FILE *fp; if ((fp = fopenWriteStream(filename, "wb+")) == NULL) return ERROR_INT("stream not opened", procName, 1); - if (pixWriteStreamJp2k(fp, pix, quality, nlevels, hint)) { + if (pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug)) { fclose(fp); return ERROR_INT("pix not written to stream", procName, 1); } @@ -469,16 +496,20 @@ FILE *fp; * quality (SNR > 0; default ~34; 0 for lossless encoding) * nlevels (<= 10) * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * 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_int32 pixWriteStreamJp2k(FILE *fp, PIX *pix, l_int32 quality, l_int32 nlevels, - l_int32 hint) + l_int32 hint, + l_int32 debug) { l_int32 w, h, d, success, snr; const char *opjVersion; @@ -513,11 +544,11 @@ opj_image_t *image = NULL; L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); return 1; } - if ((opjVersion[2] - 0x30) != OpjMinor) { + if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { L_ERROR("version %s: differs from minor = %d\n", - procName, opjVersion, OpjMinor); - return 1; - } + procName, opjVersion, OPJ_VERSION_MINOR); + return 1; + } /* Remove colormap if it exists; result is 8 or 32 bpp */ pixGetDimensions(pix, &w, &h, &d); @@ -563,19 +594,35 @@ opj_image_t *image = NULL; } /* Get the encoder handle */ - l_codec = opj_create_compress(OPJ_CODEC_JP2); + if ((l_codec = opj_create_compress(OPJ_CODEC_JP2)) == NULL) { + opj_image_destroy(image); + 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 */ - opj_setup_encoder(l_codec, ¶meters, image); + if (!opj_setup_encoder(l_codec, ¶meters, image)) { + opj_destroy_codec(l_codec); + opj_image_destroy(image); + 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_stream_destroy(l_stream); opj_destroy_codec(l_codec); opj_image_destroy(image); + FREE(parameters.cp_comment); return ERROR_INT("failed to open l_stream\n", procName, 1); } @@ -584,12 +631,14 @@ opj_image_t *image = NULL; opj_stream_destroy(l_stream); opj_destroy_codec(l_codec); opj_image_destroy(image); + 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); + FREE(parameters.cp_comment); return ERROR_INT("opj_encode failed\n", procName, 1); } success = opj_end_compress(l_codec, l_stream); @@ -706,20 +755,24 @@ extern FILE *fmemopen(void *data, size_t size, const char *mode); * reduction (scaling factor: 1, 2, 4, 8) * box ( for extracting a subregion), can be null * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * debug (output callback messages, etc) * Return: pix, or null on error * * Notes: - * (1) See pixReadJp2k() for usage. + * (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 hint, + l_int32 debug) { -l_int32 ret; -l_uint8 *comment; FILE *fp; PIX *pix; @@ -728,16 +781,17 @@ PIX *pix; if (!data) return (PIX *)ERROR_PTR("data not defined", procName, NULL); -#if HAVE_FMEMOPEN - if ((fp = fmemopen((l_uint8 *)data, size, "r")) == NULL) +#if 0 /* Avoid the crash for now */ + if ((fp = fmemopen((void *)data, size, "r")) == NULL) return (PIX *)ERROR_PTR("stream not opened", procName, NULL); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (PIX *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(data, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ - pix = pixReadStreamJp2k(fp, reduction, box, hint); + pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); fclose(fp); if (!pix) L_ERROR("pix not read\n", procName); return pix; @@ -753,6 +807,7 @@ PIX *pix; * quality (SNR > 0; default ~34; 0 for lossless encoding) * nlevels (0 for default) * hint (a bitwise OR of L_JP2K_* values; 0 for default) + * debug (output callback messages, etc) * Return: 0 if OK, 1 on error * * Notes: @@ -765,7 +820,8 @@ pixWriteMemJp2k(l_uint8 **pdata, PIX *pix, l_int32 quality, l_int32 nlevels, - l_int32 hint) + l_int32 hint, + l_int32 debug) { l_int32 ret; FILE *fp; @@ -784,11 +840,12 @@ FILE *fp; #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); + ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); - ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); rewind(fp); *pdata = l_binaryReadStream(fp, psize); #endif /* HAVE_FMEMOPEN */ @@ -853,12 +910,12 @@ opj_stream_t *l_stream; if (!l_stream) return (opj_stream_t *)ERROR_PTR("stream not made", procName, NULL); -#if MINOR == 0 - opj_stream_set_user_data(l_stream, fp); +#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 /* MINOR */ + 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); @@ -872,7 +929,6 @@ opj_stream_t *l_stream; return l_stream; } - /* --------------------------------------------*/ #endif /* HAVE_LIBJPEG */ /* --------------------------------------------*/ diff --git a/liblept/src/jp2kiostub.c b/liblept/src/jp2kiostub.c index 79a6139..245a47d 100644 --- a/liblept/src/jp2kiostub.c +++ b/liblept/src/jp2kiostub.c @@ -43,14 +43,15 @@ /* ----------------------------------------------------------------------*/ PIX * pixReadJp2k(const char *filename, l_uint32 reduction, BOX *box, - l_int32 hint) + 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) +PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box, + l_int32 hint, l_int32 debug) { return (PIX * )ERROR_PTR("function not present", "pixReadStreamJp2k", NULL); } @@ -58,7 +59,7 @@ PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box, l_int32 hint) /* ----------------------------------------------------------------------*/ l_int32 pixWriteJp2k(const char *filename, PIX *pix, l_int32 quality, - l_int32 nlevels, l_int32 hint) + l_int32 nlevels, l_int32 hint, l_int32 debug) { return ERROR_INT("function not present", "pixWriteJp2k", 1); } @@ -66,7 +67,7 @@ l_int32 pixWriteJp2k(const char *filename, PIX *pix, l_int32 quality, /* ----------------------------------------------------------------------*/ l_int32 pixWriteStreamJp2k(FILE *fp, PIX *pix, l_int32 quality, - l_int32 nlevels, l_int32 hint) + l_int32 nlevels, l_int32 hint, l_int32 debug) { return ERROR_INT("function not present", "pixWriteStreamJp2k", 1); } @@ -74,7 +75,7 @@ l_int32 pixWriteStreamJp2k(FILE *fp, PIX *pix, l_int32 quality, /* ----------------------------------------------------------------------*/ PIX * pixReadMemJp2k(const l_uint8 *data, size_t size, l_uint32 reduction, - BOX *box, l_int32 hint) + BOX *box, l_int32 hint, l_int32 debug) { return (PIX * )ERROR_PTR("function not present", "pixReadMemJp2k", NULL); } @@ -82,7 +83,8 @@ PIX * pixReadMemJp2k(const l_uint8 *data, size_t size, l_uint32 reduction, /* ----------------------------------------------------------------------*/ l_int32 pixWriteMemJp2k(l_uint8 **pdata, size_t *psize, PIX *pix, - l_int32 quality, l_int32 nlevels, l_int32 hint) + l_int32 quality, l_int32 nlevels, l_int32 hint, + l_int32 debug) { return ERROR_INT("function not present", "pixWriteMemJp2k", 1); } diff --git a/liblept/src/jpegio.c b/liblept/src/jpegio.c index abc5d1c..06185da 100644 --- a/liblept/src/jpegio.c +++ b/liblept/src/jpegio.c @@ -175,9 +175,8 @@ struct callback_data { * pixReadJpeg() * * Input: filename - * colormap flag (0 means return RGB image if color; - * 1 means create a colormap and return - * an 8 bpp colormapped image if color) + * cmapflag (0 for no colormap in returned pix; + * 1 to return an 8 bpp cmapped pix if spp = 3 or 4) * reduction (scaling factor: 1, 2, 4 or 8) * &nwarn ( number of warnings about * corrupted data) @@ -208,7 +207,7 @@ struct callback_data { */ PIX * pixReadJpeg(const char *filename, - l_int32 cmflag, + l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint) @@ -223,14 +222,14 @@ PIX *pix; if (pnwarn) *pnwarn = 0; if (!filename) return (PIX *)ERROR_PTR("filename not defined", procName, NULL); - if (cmflag != 0 && cmflag != 1) - cmflag = 0; /* default */ + 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, cmflag, reduction, pnwarn, hint); + pix = pixReadStreamJpeg(fp, cmapflag, reduction, pnwarn, hint); if (pix) { ret = fgetJpegComment(fp, &comment); if (!ret && comment) @@ -249,9 +248,8 @@ PIX *pix; * pixReadStreamJpeg() * * Input: stream - * colormap flag (0 means return RGB image if color; - * 1 means create a colormap and return - * an 8 bpp colormapped image if color) + * cmapflag (0 for no colormap in returned pix; + * 1 to return an 8 bpp cmapped pix if spp = 3 or 4) * reduction (scaling factor: 1, 2, 4 or 8) * &nwarn ( number of warnings) * hint (a bitwise OR of L_JPEG_* values; 0 for default) @@ -259,11 +257,11 @@ PIX *pix; * * Usage: see pixReadJpeg() * Notes: - * (1) This does not get the jpeg comment. + * (1) The jpeg comment, if it exists, is not stored in the pix. */ PIX * pixReadStreamJpeg(FILE *fp, - l_int32 cmflag, + l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint) @@ -285,8 +283,8 @@ jmp_buf jmpbuf; /* must be local to the function */ if (pnwarn) *pnwarn = 0; if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - if (cmflag != 0 && cmflag != 1) - cmflag = 0; /* default */ + 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); @@ -325,13 +323,13 @@ jmp_buf jmpbuf; /* must be local to the function */ /* Allocate the image and a row buffer */ w = cinfo.output_width; h = cinfo.output_height; - ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmflag == 0); - cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmflag == 0); + 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 && cmflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ + if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), spp * w); pix = pixCreate(w, h, 32); } else { /* 8 bpp gray or colormapped */ @@ -349,7 +347,7 @@ jmp_buf jmpbuf; /* must be local to the function */ if (spp == 1) { /* Grayscale or colormapped */ jpeg_start_decompress(&cinfo); } else { /* Color; spp == 3 or YCCK or CMYK */ - if (cmflag == 0) { /* -- 24 bit color in 32 bit pix or YCCK/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 */ @@ -390,7 +388,7 @@ jmp_buf jmpbuf; /* must be local to the function */ } /* -- 24 bit color -- */ - if ((spp == 3 && cmflag == 0) || ycck || cmyk) { + if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { ppixel = data + i * wpl; if (spp == 3) { for (j = k = 0; j < w; j++) { @@ -988,7 +986,8 @@ PIX *pix; return (PIX *)ERROR_PTR("stream not opened", procName, NULL); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (PIX *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(data, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -1047,7 +1046,8 @@ FILE *fp; return ERROR_INT("stream not opened", procName, 1); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); fwrite(data, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -1098,7 +1098,8 @@ FILE *fp; ret = pixWriteStreamJpeg(fp, pix, quality, progressive); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); ret = pixWriteStreamJpeg(fp, pix, quality, progressive); rewind(fp); *pdata = l_binaryReadStream(fp, psize); diff --git a/liblept/src/libversions.c b/liblept/src/libversions.c index 161069d..0ce63eb 100644 --- a/liblept/src/libversions.c +++ b/liblept/src/libversions.c @@ -68,13 +68,10 @@ #include "webp/encode.h" #endif -#if HAVE_LIBJP2K /* assuming it's 2.1 */ -#include "openjpeg-2.1/openjpeg.h" +#if HAVE_LIBJP2K +#include LIBJP2K_HEADER #endif -#define stringJoinInPlace(s1, s2) \ - { tempStrP = stringJoin((s1),(s2)); FREE(s1); (s1) = tempStrP; } - /*---------------------------------------------------------------------* * Image Library Version number * @@ -84,7 +81,7 @@ * * Return: string of version numbers; e.g., * libgif 5.0.3 - * libjpeg 8b + * libjpeg 8b (libjpeg-turbo 1.3.0) * libpng 1.4.3 * libtiff 3.9.5 * zlib 1.2.5 @@ -92,101 +89,110 @@ * libopenjp2 2.1.0 * * Notes: - * (1) The caller has responsibility to free the memory. + * (1) The caller must free the memory. */ char * getImagelibVersions() { char buf[128]; l_int32 first = TRUE; - -#if HAVE_LIBJPEG - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr err; - char buffer[JMSG_LENGTH_MAX]; -#endif - char *tempStrP; - char *versionNumP; - char *nextTokenP; - char *versionStrP = stringNew(""); +char *versionNumP; +char *nextTokenP; +char *versionStrP = NULL; #if HAVE_LIBGIF first = FALSE; - stringJoinInPlace(versionStrP, "libgif "); + 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 - stringJoinInPlace(versionStrP, 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) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "libjpeg "); + stringJoinIP(&versionStrP, "libjpeg "); versionNumP = strtokSafe(buffer, " ", &nextTokenP); - stringJoinInPlace(versionStrP, versionNumP); + stringJoinIP(&versionStrP, versionNumP); FREE(versionNumP); -#endif + + #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) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "libpng "); - stringJoinInPlace(versionStrP, png_get_libpng_ver(NULL)); -#endif + stringJoinIP(&versionStrP, "libpng "); + stringJoinIP(&versionStrP, png_get_libpng_ver(NULL)); +#endif /* HAVE_LIBPNG */ #if HAVE_LIBTIFF - if (!first) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "libtiff "); + stringJoinIP(&versionStrP, "libtiff "); versionNumP = strtokSafe((char *)TIFFGetVersion(), " \n", &nextTokenP); FREE(versionNumP); versionNumP = strtokSafe(NULL, " \n", &nextTokenP); FREE(versionNumP); versionNumP = strtokSafe(NULL, " \n", &nextTokenP); - stringJoinInPlace(versionStrP, versionNumP); + stringJoinIP(&versionStrP, versionNumP); FREE(versionNumP); -#endif +#endif /* HAVE_LIBTIFF */ #if HAVE_LIBZ - if (!first) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "zlib "); - stringJoinInPlace(versionStrP, zlibVersion()); -#endif + stringJoinIP(&versionStrP, "zlib "); + stringJoinIP(&versionStrP, zlibVersion()); +#endif /* HAVE_LIBZ */ #if HAVE_LIBWEBP { l_int32 val; char buf[32]; - if (!first) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "libwebp "); + stringJoinIP(&versionStrP, "libwebp "); val = WebPGetEncoderVersion(); snprintf(buf, sizeof(buf), "%d.%d.%d", val >> 16, (val >> 8) & 0xff, val & 0xff); - stringJoinInPlace(versionStrP, buf); + stringJoinIP(&versionStrP, buf); } -#endif +#endif /* HAVE_LIBWEBP */ #if HAVE_LIBJP2K { const char *version; - if (!first) stringJoinInPlace(versionStrP, " : "); + if (!first) stringJoinIP(&versionStrP, " : "); first = FALSE; - stringJoinInPlace(versionStrP, "libopenjp2 "); + stringJoinIP(&versionStrP, "libopenjp2 "); version = opj_version(); - stringJoinInPlace(versionStrP, version); + stringJoinIP(&versionStrP, version); } -#endif +#endif /* HAVE_LIBJP2K */ - stringJoinInPlace(versionStrP, "\n"); + stringJoinIP(&versionStrP, "\n"); return versionStrP; } diff --git a/liblept/src/numabasic.c b/liblept/src/numabasic.c index afae6f9..51215c8 100644 --- a/liblept/src/numabasic.c +++ b/liblept/src/numabasic.c @@ -874,6 +874,10 @@ numaGetParameters(NUMA *na, { 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); diff --git a/liblept/src/numafunc1.c b/liblept/src/numafunc1.c index be39047..5b00574 100644 --- a/liblept/src/numafunc1.c +++ b/liblept/src/numafunc1.c @@ -2756,11 +2756,10 @@ NUMA *naindex; PROCNAME("numaSortPair"); - if (!pnasx) - return ERROR_INT("&nasx not defined", procName, 1); - if (!pnasy) - return ERROR_INT("&nasy not defined", procName, 1); - *pnasx = *pnasy = NULL; + 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) @@ -3073,13 +3072,12 @@ NUMA *nasort; PROCNAME("numaGetMode"); - if (!na) - return ERROR_INT("na not defined", procName, 1); + if (pcount) *pcount = 0; if (!pval) return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (pcount) *pcount = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); if ((n = numaGetCount(na)) == 0) return 1; @@ -3150,15 +3148,15 @@ NUMA *navar; PROCNAME("numaGetMedianVar"); + if (pmedval) *pmedval = 0.0; if (!pmedvar) return ERROR_INT("&medvar not defined", procName, 1); - *pmedvar = 0.0; /* init */ + *pmedvar = 0.0; if (!na) return ERROR_INT("na not defined", procName, 1); numaGetMedian(na, &medval); if (pmedval) *pmedval = medval; - n = numaGetCount(na); navar = numaCreate(n); for (i = 0; i < n; i++) { diff --git a/liblept/src/numafunc2.c b/liblept/src/numafunc2.c index eaab6b6..843ba7c 100644 --- a/liblept/src/numafunc2.c +++ b/liblept/src/numafunc2.c @@ -39,6 +39,7 @@ * NUMA *numaWindowedMean() * NUMA *numaWindowedMeanSquare() * l_int32 numaWindowedVariance() + * NUMA *numaWindowedMedian() * NUMA *numaConvertToInt() * * Histogram generation and statistics @@ -606,12 +607,14 @@ 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); - if (!pnav && !pnarv) - return ERROR_INT("neither &nav nor &narv are defined", procName, 1); nm = numaGetCount(nam); nms = numaGetCount(nams); if (nm != nms) @@ -642,6 +645,58 @@ NUMA *nav, *narv; /* variance and square root of variance */ } +/*! + * numaWindowedMedian() + * + * Input: nas + * 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, just return a copy. + * (3) If the requested filter is too large, it is reduced in size. + * (4) 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 > (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; +} + + /*! * numaConvertToInt() * @@ -1027,7 +1082,8 @@ NUMA *nad; * Input: na (an arbitrary set of numbers; not ordered and not * a histogram) * maxbins (the maximum number of bins to be allowed in - * the histogram; use 0 for consecutive integer bins) + * the histogram; use an integer larger than the + * largest number in @na for consecutive integer bins) * &min ( min value of set) * &max ( max value of set) * &mean ( mean value of set) @@ -1087,8 +1143,10 @@ NUMA *nah; if (pmin) *pmin = 0.0; if (pmax) *pmax = 0.0; if (pmean) *pmean = 0.0; - if (pmedian) *pmedian = 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) @@ -1304,8 +1362,6 @@ NUMA *nan, *nar; *pnay = NULL; if (!nasy) return ERROR_INT("nasy not defined", procName, 1); - if (!pnay) - return ERROR_INT("&nay not defined", procName, 1); if ((n = numaGetCount(nasy)) == 0) return ERROR_INT("no bins in nas", procName, 1); @@ -1516,12 +1572,16 @@ 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); - if (!pnarbin && !pnam && !pnar && !pnabb) - return ERROR_INT("no output requested", procName, 1); /* Get cumulative normalized histogram (rank vs intensity value). * For a normalized histogram from an 8 bpp grayscale image @@ -1748,6 +1808,12 @@ 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); @@ -2153,10 +2219,10 @@ NUMA *nat; PROCNAME("numaCountReversals"); - if (!pnr && !pnrpl) - return ERROR_INT("neither &nr nor &nrpl are defined", procName, 1); if (pnr) *pnr = 0; if (pnrpl) *pnrpl = 0.0; + if (!pnr && !pnrpl) + return ERROR_INT("neither &nr nor &nrpl are defined", procName, 1); if (!nas) return ERROR_INT("nas not defined", procName, 1); @@ -2214,6 +2280,9 @@ 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); @@ -2252,7 +2321,7 @@ NUMA *nat, *nac; istart = i; inrun = TRUE; } - continue; + continue; } if (inrun && (val != maxval)) { iend = i - 1; @@ -2298,8 +2367,8 @@ NUMA *nat, *nac; #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); + 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); @@ -2514,10 +2583,13 @@ l_float32 bestwidth, bestshift, bestscore; PROCNAME("numaEvalBestHaarParameters"); - if (!nas) - return ERROR_INT("nas not defined", procName, 1); + 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 = 0.0; delwidth = (maxwidth - minwidth) / (nwidth - 1.0); diff --git a/liblept/src/paintcmap.c b/liblept/src/paintcmap.c index 6c92ad6..e628194 100644 --- a/liblept/src/paintcmap.c +++ b/liblept/src/paintcmap.c @@ -35,6 +35,7 @@ * Repaint non-white pixels in region * l_int32 pixColorGrayRegionsCmap() * l_int32 pixColorGrayCmap() + * l_int32 pixColorGrayMaskedCmap() * l_int32 addColorizedGrayToCmap() * * Repaint selected pixels through mask @@ -240,10 +241,10 @@ PIXCMAP *cmap; nc = pixcmapGetCount(cmap); if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) return ERROR_INT("no room; cmap full", procName, 1); - if ((map = numaGetIArray(na)) == NULL) { - numaDestroy(&na); + map = numaGetIArray(na); + numaDestroy(&na); + if (!map) return ERROR_INT("map not made", procName, 1); - } pixGetDimensions(pixs, &w, &h, NULL); data = pixGetData(pixs); @@ -274,7 +275,6 @@ PIXCMAP *cmap; } FREE(map); - numaDestroy(&na); return 0; } @@ -361,6 +361,94 @@ PIXCMAP *cmap; } +/*! + * pixColorGrayMaskedCmap() + * + * Input: pixs (8 bpp, with colormap) + * pixm (1 bpp mask, through which to apply color) + * type (L_PAINT_LIGHT, L_PAINT_DARK) + * 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_int32 +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); + } + } + + FREE(map); + return 0; +} + + /*! * addColorizedGrayToCmap() * diff --git a/liblept/src/parseprotos.c b/liblept/src/parseprotos.c index 279a2ab..f9285b8 100644 --- a/liblept/src/parseprotos.c +++ b/liblept/src/parseprotos.c @@ -46,8 +46,7 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; /* max token size */ static l_int32 getNextNonCommentLine(SARRAY *sa, l_int32 start, l_int32 *pnext); static l_int32 getNextNonBlankLine(SARRAY *sa, l_int32 start, l_int32 *pnext); diff --git a/liblept/src/pdfio1.c b/liblept/src/pdfio1.c index c6f5672..a13f986 100644 --- a/liblept/src/pdfio1.c +++ b/liblept/src/pdfio1.c @@ -1252,8 +1252,7 @@ pixWriteStreamPdf(FILE *fp, const char *title) { l_uint8 *data; -l_int32 ret; -size_t nbytes; +size_t nbytes, nbytes_written; PROCNAME("pixWriteStreamPdf"); @@ -1265,10 +1264,10 @@ size_t nbytes; if (pixWriteMemPdf(&data, &nbytes, pix, res, title) != 0) return ERROR_INT("pdf data not made", procName, 1); - ret = fwrite(data, 1, nbytes, fp); + nbytes_written = fwrite(data, 1, nbytes, fp); FREE(data); - if (ret) - return ERROR_INT("pdf data not written to stream", procName, 1); + if (nbytes != nbytes_written) + return ERROR_INT("failure writing pdf data to stream", procName, 1); return 0; } diff --git a/liblept/src/pdfio2.c b/liblept/src/pdfio2.c index 2f504a2..449b932 100644 --- a/liblept/src/pdfio2.c +++ b/liblept/src/pdfio2.c @@ -446,7 +446,7 @@ NUMAA *naa_objs; /* object mapping numbers to new values */ * l_generateCIDataForPdf() * * Input: fname - * pix ; can be null) * quality (for jpeg if transcoded; 75 is standard) * &cid ( compressed data) * Return: 0 if OK, 1 on error @@ -489,14 +489,14 @@ PIX *pixt; } else if (format == IFF_JP2) { cid = l_generateJp2kData(fname); } else if (format == IFF_PNG) { /* use Jeff's special function for png */ - cid = l_generateFlateDataPdf(fname); + cid = l_generateFlateDataPdf(fname, pix); } else { /* any other format ... */ if (!pix) pixt = pixRead(fname); else pixt = pixClone(pix); if (!pixt) - return ERROR_INT("fname not defined", procName, 1); + return ERROR_INT("pixt not made", procName, 1); selectDefaultPdfEncoding(pixt, &type); pixGenerateCIData(pixt, type, quality, 0, &cid); pixDestroy(&pixt); @@ -514,15 +514,21 @@ PIX *pixt; * l_generateFlateDataPdf() * * Input: fname (preferably png) + * pix (; 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) +l_generateFlateDataPdf(const char *fname, + PIX *pixs) { l_uint8 *pngcomp = NULL; /* entire PNG compressed file */ l_uint8 *datacomp = NULL; /* gzipped raster data */ @@ -538,7 +544,7 @@ l_int32 xres, yres; size_t nbytescomp = 0, nbytespng = 0; FILE *fp; L_COMP_DATA *cid; -PIX *pixs; +PIX *pix; PIXCMAP *cmap = NULL; PROCNAME("l_generateFlateDataPdf"); @@ -547,15 +553,31 @@ PIXCMAP *cmap = NULL; return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); findFileFormat(fname, &format); - if (format == IFF_PNG) + spp = 0; /* init to spp != 4 if not png */ + interlaced = 0; /* initialize to no interlacing */ + if (format == IFF_PNG) { isPngInterlaced(fname, &interlaced); + readHeaderPng(fname, NULL, NULL, NULL, &spp, NULL); + } - /* If either interlaced png or another format, transcode to flate */ - if (interlaced || format != IFF_PNG) { - if ((pixs = pixRead(fname)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("pixs not made", procName, NULL); - cid = pixGenerateFlateData(pixs, 0); - pixDestroy(&pixs); + /* PDF is capable of inlining some types of PNG files, but not all + of them. We need to transcode anything with interlacing or an + alpha channel. + + 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 || 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; } @@ -1007,6 +1029,13 @@ PIX *pixs; * Input: pixs * 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, @@ -1020,7 +1049,7 @@ 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; 4-rgba */ +l_int32 spp; /* samples/pixel: 1-grayscale/cmap); 3-rgb */ l_int32 w, h, d, cmapflag; l_int32 ncmapbytes85 = 0; l_int32 nbytes85 = 0; @@ -1049,7 +1078,7 @@ PIXCMAP *cmap; } else { pixt = pixClone(pixs); } - spp = (d == 32) ? 3 : 1; + spp = (d == 32) ? 3 : 1; /* ignores alpha */ bps = (d == 32) ? 8 : d; /* Extract and encode the colormap data as both ascii85 and hexascii */ @@ -1611,7 +1640,7 @@ SARRAY *sa; sa = lpd->saprex; cmindex = 6 + lpd->n; /* starting value */ for (i = 0; i < lpd->n; i++) { - pstr = NULL; + pstr = cstr = NULL; if ((cid = pdfdataGetCid(lpd, i)) == NULL) return ERROR_INT("cid not found", procName, 1); @@ -1638,7 +1667,7 @@ SARRAY *sa; else if (cid->spp == 3) cstr = stringNew("/ColorSpace /DeviceRGB"); else - L_ERROR("spp!= 1 && spp != 3\n", procName); + L_ERROR("in jpeg: spp != 1 && spp != 3\n", procName); bstr = stringNew("/BitsPerComponent 8"); fstr = stringNew("/Filter /DCTDecode"); } else if (cid->type == L_JP2K_ENCODE) { @@ -1647,7 +1676,7 @@ SARRAY *sa; else if (cid->spp == 3) cstr = stringNew("/ColorSpace /DeviceRGB"); else - L_ERROR("spp!= 1 && spp != 3\n", procName); + L_ERROR("in jp2k: spp != 1 && spp != 3\n", procName); bstr = stringNew("/BitsPerComponent 8"); fstr = stringNew("/Filter /JPXDecode"); } else { /* type == L_FLATE_ENCODE */ @@ -1663,7 +1692,8 @@ SARRAY *sa; else if (cid->spp == 3) cstr = stringNew("/ColorSpace /DeviceRGB"); else - L_ERROR("unknown colorspace\n", procName); + L_ERROR("unknown colorspace: spp = %d\n", + procName, cid->spp); } snprintf(buff, sizeof(buff), "/BitsPerComponent %d", cid->bps); bstr = stringNew(buff); @@ -1674,8 +1704,9 @@ SARRAY *sa; "<<\n" " /Columns %d\n" " /Predictor 14\n" + " /Colors %d\n" " /BitsPerComponent %d\n" - ">>\n", cid->w, cid->bps); + ">>\n", cid->w, cid->spp, cid->bps); pstr = stringNew(buff); } } diff --git a/liblept/src/pdfio2stub.c b/liblept/src/pdfio2stub.c index ecd1f8f..3722de8 100644 --- a/liblept/src/pdfio2stub.c +++ b/liblept/src/pdfio2stub.c @@ -65,7 +65,7 @@ l_int32 l_generateCIDataForPdf(const char *fname, PIX *pix, l_int32 quality, /* ----------------------------------------------------------------------*/ -L_COMP_DATA * l_generateFlateDataPdf(const char *fname) +L_COMP_DATA * l_generateFlateDataPdf(const char *fname, PIX *pix) { return (L_COMP_DATA *)ERROR_PTR("function not present", "l_generateFlateDataPdf", NULL); diff --git a/liblept/src/pix1.c b/liblept/src/pix1.c index e8127ec..072286e 100644 --- a/liblept/src/pix1.c +++ b/liblept/src/pix1.c @@ -521,7 +521,6 @@ PIX *pix; if ((pix = *ppix) == NULL) return; - pixFree(pix); *ppix = NULL; return; @@ -845,7 +844,7 @@ PIX *pixs; /*! * pixSwapAndDestroy() * - * Input: &pixd ( input pixd can be null, + * Input: &pixd ( input pixd can be null, * and it must be different from pixs) * &pixs (will be nulled after the swap) * Return: 0 if OK, 1 on error @@ -1246,6 +1245,10 @@ pixGetResolution(PIX *pix, { 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; @@ -1698,6 +1701,7 @@ void **lines; PROCNAME("pixGetLinePtrs"); + if (psize) *psize = 0; if (!pix) return (void **)ERROR_PTR("pix not defined", procName, NULL); diff --git a/liblept/src/pix2.c b/liblept/src/pix2.c index c69f028..5ea16de 100644 --- a/liblept/src/pix2.c +++ b/liblept/src/pix2.c @@ -184,7 +184,7 @@ l_uint32 *line, *data; PROCNAME("pixGetPixel"); if (!pval) - return ERROR_INT("pval not defined", procName, 1); + return ERROR_INT("&val not defined", procName, 1); *pval = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); @@ -256,7 +256,6 @@ l_uint32 *line, *data; if (!pix) return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); if (x < 0 || x >= w) return ERROR_INT("x out of bounds", procName, 1); @@ -320,6 +319,11 @@ 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); @@ -407,8 +411,13 @@ 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 (!pval) - return ERROR_INT("pval not defined", procName, 1); + return ERROR_INT("&val not defined", procName, 1); *pval = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); @@ -449,7 +458,6 @@ l_uint32 *line, *data; if (!pix) return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); if (x < 0 || x >= w) return ERROR_INT("x out of bounds", procName, 1); @@ -507,7 +515,6 @@ l_uint32 *line, *data; if (!pix) return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); if (x < 0 || x >= w) return ERROR_INT("x out of bounds", procName, 1); @@ -3029,10 +3036,10 @@ 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); - *pdata = NULL; - *pnbytes = 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixGetDimensions(pixs, &w, &h, &d); @@ -3168,12 +3175,12 @@ l_int32 w, h; if (!pix || pixGetDepth(pix) != 8) return (l_uint8 **)ERROR_PTR("pix not defined or not 8 bpp", procName, NULL); - if (pixGetColormap(pix)) - return (l_uint8 **)ERROR_PTR("pix has colormap", 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); } diff --git a/liblept/src/pix3.c b/liblept/src/pix3.c index 065d9bf..38f5ce8 100644 --- a/liblept/src/pix3.c +++ b/liblept/src/pix3.c @@ -45,6 +45,8 @@ * PIX *pixPaintSelfThroughMask() * PIX *pixMakeMaskFromLUT() * PIX *pixSetUnderTransparency() + * PIX *pixMakeAlphaFromMask() + * l_int32 pixGetColorNearMaskBoundary() * * One and two-image boolean operations on arbitrary depth images * PIX *pixInvert() @@ -90,17 +92,20 @@ * Mirrored tiling * PIX *pixMirroredTiling() * + * Representative tile near but outside region + * l_int32 pixFindRepCloseTile() + * * Static helper function - * static l_int32 findTilePatchCenter() + * static BOXA *findTileRegionsForSearch() */ #include #include #include "allheaders.h" -static l_int32 findTilePatchCenter(PIX *pixs, BOX *box, l_int32 dir, - l_uint32 targdist, l_uint32 *pdist, - l_int32 *pxc, l_int32 *pyc); +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 @@ -604,10 +609,10 @@ l_uint32 *data, *datam, *line, *linem; PROCNAME("pixPaintThroughMask"); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); 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); @@ -705,8 +710,11 @@ l_uint32 *data, *datam, *line, *linem; * Input: pixd (8 bpp gray or 32 bpp rgb; not colormapped) * pixm (1 bpp mask) * x, y (origin of pixm relative to pixd; must not be negative) - * tilesize (requested size for tiling) - * searchdir (L_HORIZ, L_VERT) + * searchdir (L_HORIZ, L_VERT or L_BOTH_DIRECTIONS) + * mindist (min distance of nearest tile edge to box; >= 0) + * tilesize (requested size for tiling; may be reduced) + * ntiles (number of tiles tested in each row/column) + * distblend (distance outside the fg used for blending with pixs) * Return: 0 if OK; 1 on error * * Notes: @@ -715,31 +723,62 @@ l_uint32 *data, *datam, *line, *linem; * (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) The tilesize is the the requested size for tiling. The + * (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. and the distance at which the tile - * center is located. - * (5) searchdir is the direction with respect to the b.b. of each - * mask component, from which the square patch is chosen and - * tiled onto the image, clipped by the mask component. - * (6) Specifically, a mirrored tiling, generated from pixd, - * is used to construct the pixels that are painted onto - * pixd through pixm. + * 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_int32 pixPaintSelfThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, + l_int32 searchdir, + l_int32 mindist, l_int32 tilesize, - l_int32 searchdir) + l_int32 ntiles, + l_int32 distblend) { -l_int32 w, h, d, wm, hm, dm, i, n, xc, yc, bx, by, bw, bh; -l_int32 depth, cctilesize; -l_uint32 dist, minside, retval; -BOX *box, *boxt; +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 *pix, *pixf, *pixdf, *pixt, *pixc; +PIX *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5; PIXA *pixa; PROCNAME("pixPaintSelfThroughMask"); @@ -758,10 +797,13 @@ PIXA *pixa; 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 (tilesize < 1) - return ERROR_INT("tilesize must be >= 1", procName, 1); - if (searchdir != L_HORIZ && searchdir != L_VERT) - return ERROR_INT("searchdir not in {L_HORIZ, L_VERT}", 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) { @@ -780,53 +822,86 @@ PIXA *pixa; boxaDestroy(&boxa); return 1; } + boxaDestroy(&boxa); - /* Get distance function for the mask */ - pixInvert(pixf, pixf); - depth = (tilesize < 256) ? 8 : 16; - pixdf = pixDistanceFunction(pixf, 4, depth, L_BOUNDARY_BG); - pixDestroy(&pixf); - - /* For each c.c., generate a representative tile for texturizing - * and apply it through the mask. The input 'tilesize' is the - * requested value. findTilePatchCenter() returns the distance - * at which this patch can safely be found. */ + /* 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++) { - pix = pixaGetPix(pixa, i, L_CLONE); - box = pixaGetBox(pixa, i, L_CLONE); + 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); - findTilePatchCenter(pixdf, box, searchdir, L_MIN(minside, tilesize), - &dist, &xc, &yc); - cctilesize = L_MIN(tilesize, dist); /* for this c.c. */ - if (cctilesize < 1) { - L_WARNING("region not found!\n", procName); - pixDestroy(&pix); + 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); - retval = 1; continue; } - /* Extract the selected square from pixd, and generate - * an image the size of the b.b. of the c.c., which - * is then painted through the c.c. mask. */ - boxt = boxCreate(L_MAX(0, xc - dist / 2), L_MAX(0, yc - dist / 2), - cctilesize, cctilesize); - pixt = pixClipRectangle(pixd, boxt, NULL); - pixc = pixMirroredTiling(pixt, bw, bh); - pixCombineMaskedGeneral(pixd, pixc, pix, bx, by); - pixDestroy(&pix); - pixDestroy(&pixt); - pixDestroy(&pixc); + /* 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); - boxDestroy(&boxt); } - pixDestroy(&pixdf); pixaDestroy(&pixa); - boxaDestroy(&boxa); + pixDestroy(&pixf); return retval; } @@ -982,6 +1057,182 @@ PIX *pixg, *pixm, *pixt, *pixd; } +/*! + * pixMakeAlphaFromMask() + * + * Input: pixs (1 bpp) + * dist (blending distance; typically 10 - 30) + * &box (, 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; +} + + +/*! + * pixGetColorNearMaskBoundary() + * + * Input: pixs (32 bpp rgb) + * pixm (1 bpp mask, full image) + * box (region of mask; typically b.b. of a component) + * dist (distance into BG from mask boundary to use) + * &pval ( average pixel value) + * 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_int32 +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"); + pixWrite("/tmp/masknear/input.png", pix1, IFF_PNG); + pixWrite("/tmp/masknear/adjusted.png", pix2, IFF_PNG); + pixWrite("/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 * *-------------------------------------------------------------*/ @@ -1312,7 +1563,7 @@ l_uint32 *data, *line; PROCNAME("pixZero"); if (!pempty) - return ERROR_INT("pempty not defined", procName, 1); + return ERROR_INT("&empty not defined", procName, 1); *pempty = 1; if (!pix) return ERROR_INT("pix not defined", procName, 1); @@ -1323,7 +1574,7 @@ l_uint32 *data, *line; data = pixGetData(pix); fullwords = w / 32; endbits = w & 31; - endmask = 0xffffffff << (32 - endbits); + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); for (i = 0; i < h; i++) { line = data + wpl * i; @@ -1360,7 +1611,7 @@ l_int32 w, h, count; PROCNAME("pixForegroundFraction"); if (!pfract) - return ERROR_INT("pfract not defined", procName, 1); + 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); @@ -1437,7 +1688,7 @@ l_uint32 *data; PROCNAME("pixCountPixels"); if (!pcount) - return ERROR_INT("pcount not defined", procName, 1); + 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); @@ -1452,7 +1703,7 @@ l_uint32 *data; data = pixGetData(pix); fullwords = w >> 5; endbits = w & 31; - endmask = 0xffffffff << (32 - endbits); + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); sum = 0; for (i = 0; i < h; i++, data += wpl) { @@ -1683,7 +1934,7 @@ l_uint32 *line; PROCNAME("pixCountPixelsInRow"); if (!pcount) - return ERROR_INT("pcount not defined", procName, 1); + 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); @@ -1695,7 +1946,7 @@ l_uint32 *line; line = pixGetData(pix) + row * wpl; fullwords = w >> 5; endbits = w & 31; - endmask = 0xffffffff << (32 - endbits); + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); if (!tab8) tab = makePixelSumTab8(); @@ -1807,7 +2058,7 @@ l_uint32 *line, *data; PROCNAME("pixThresholdPixelSum"); if (!pabove) - return ERROR_INT("pabove not defined", procName, 1); + 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); @@ -2113,7 +2364,7 @@ l_float64 ave; PROCNAME("pixAverageInRect"); if (!pave) - return ERROR_INT("pave not defined", procName, 1); + return ERROR_INT("&ave not defined", procName, 1); *pave = 0; if (!pix) return ERROR_INT("pix not defined", procName, 1); @@ -2298,8 +2549,8 @@ l_float64 sum1, sum2, norm, ave, var; PROCNAME("pixVarianceInRect"); if (!prootvar) - return ERROR_INT("prootvar not defined", procName, 1); - *prootvar = 0; + 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); @@ -2494,8 +2745,8 @@ l_float64 norm, sum; PROCNAME("pixAbsDiffInRect"); if (!pabsdiff) - return ERROR_INT("pave not defined", procName, 1); - *pabsdiff = 0; + 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) @@ -2570,8 +2821,8 @@ l_uint32 val0, val1; PROCNAME("pixAbsDiffOnLine"); if (!pabsdiff) - return ERROR_INT("pave not defined", procName, 1); - *pabsdiff = 0; + 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) { @@ -2760,131 +3011,255 @@ PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix; /*! - * findTilePatchCenter() + * pixFindRepCloseTile() * - * Input: pixs (8 or 16 bpp; distance function of a binary mask) + * Input: pixs (32 bpp rgb) * box (region of pixs to search around) * searchdir (L_HORIZ or L_VERT; direction to search) - * targdist (desired distance of selected patch center from fg) - * &dist ( actual distance of selected location) - * &xc, &yc ( location of selected patch center) + * mindist (min distance of selected tile edge from box; >= 0) + * tsize (tile size; > 1; even; typically ~50) + * ntiles (number of tiles tested in each row/column) + * &boxtile ( region of best tile) + * debug (1 for debug output) * Return: 0 if OK, 1 on error * * Notes: - * (1) This looks for a patch of non-masked image, that is outside - * but near the input box. The input pixs is a distance - * function giving the distance from the fg in a binary mask. - * (2) The target distance implicitly specifies a desired size - * for the patch. The location of the center of the patch, - * and the actual distance from fg are returned. - * (3) If the target distance is larger than 255, a 16-bit distance - * transform is input. - * (4) It is assured that a square centered at (xc, yc) and of - * size 'dist' will not intersect with the fg of the binary - * mask that was used to generate pixs. - * (5) We search away from the component, in approximately - * the center 1/3 of its dimension. This gives a better chance - * of finding patches that are close to the component. + * (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.. */ -static l_int32 -findTilePatchCenter(PIX *pixs, - BOX *box, - l_int32 searchdir, - l_uint32 targdist, - l_uint32 *pdist, - l_int32 *pxc, - l_int32 *pyc) +l_int32 +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, bx, by, bw, bh, left, right, top, bot, i, j; -l_int32 xstart, xend, ystart, yend; -l_uint32 val, maxval; - - PROCNAME("findTilePatchCenter"); - - if (!pdist || !pxc || !pyc) - return ERROR_INT("&pdist, &pxc, &pyc not all defined", procName, 1); - *pdist = *pxc = *pyc = 0; +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); - boxGetGeometry(box, &bx, &by, &bw, &bh); + 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; +} + + +/*! + * findTileRegionsForSearch() + * + * Input: box (region of Pix to search around) + * w, h (dimensions of Pix) + * searchdir (L_HORIZ or L_VERT; direction to search) + * mindist (min distance of selected tile edge from box; >= 0) + * tsize (tile size; > 1; even; typically ~50) + * 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 */ - ystart = by + bh / 3; - yend = by + 2 * bh / 3; - maxval = 0; - if (left > right) { /* search to left */ - for (j = bx - 1; j >= 0; j--) { - for (i = ystart; i <= yend; i++) { - pixGetPixel(pixs, j, i, &val); - if (val > maxval) { - maxval = val; - *pxc = j; - *pyc = i; - *pdist = val; - if (val >= targdist) - return 0; - } - } - } - } else { /* search to right */ - for (j = bx + bw; j < w; j++) { - for (i = ystart; i <= yend; i++) { - pixGetPixel(pixs, j, i, &val); - if (val > maxval) { - maxval = val; - *pxc = j; - *pyc = i; - *pdist = val; - if (val >= targdist) - return 0; - } - } + 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 { /* searchdir == L_VERT */ - top = by; /* distance above box */ + } else { /* L_VERT */ + /* Find the tile parameters for the search */ + top = by; /* distance above box */ bot = h - by - bh + 1; /* distance below box */ - xstart = bx + bw / 3; - xend = bx + 2 * bw / 3; - maxval = 0; - if (top > bot) { /* search above */ - for (i = by - 1; i >= 0; i--) { - for (j = xstart; j <=xend; j++) { - pixGetPixel(pixs, j, i, &val); - if (val > maxval) { - maxval = val; - *pxc = j; - *pyc = i; - *pdist = val; - if (val >= targdist) - return 0; - } - } - } - } else { /* search below */ - for (i = by + bh; i < h; i++) { - for (j = xstart; j <=xend; j++) { - pixGetPixel(pixs, j, i, &val); - if (val > maxval) { - maxval = val; - *pxc = j; - *pyc = i; - *pdist = val; - if (val >= targdist) - return 0; - } - } + 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); } } } - - - pixGetPixel(pixs, *pxc, *pyc, pdist); - return 0; + return boxa; } + diff --git a/liblept/src/pix4.c b/liblept/src/pix4.c index 50cbb02..a300d38 100644 --- a/liblept/src/pix4.c +++ b/liblept/src/pix4.c @@ -215,7 +215,6 @@ PIX *pixg; if (!pixm) return pixGetGrayHistogram(pixs, factor); - if (!pixs) return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) @@ -292,7 +291,6 @@ PIX *pixg; if (!box) return pixGetGrayHistogram(pixs, factor); - if (!pixs) return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) @@ -714,7 +712,6 @@ NUMA *na; if (!box) return pixGetCmapHistogram(pixs, factor); - if (!pixs) return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetColormap(pixs) == NULL) @@ -822,9 +819,9 @@ PIXCMAP *cmap; * can be < 0; these values are ignored if pixm is null) * factor (subsampling factor; integer >= 1) * rank (between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest) - * &rval ( red component val for to input rank) - * &gval ( green component val for to input rank) - * &bval ( blue component val for to input rank) + * &rval ( red component val for input rank) + * &gval ( green component val for input rank) + * &bval ( blue component val for input rank) * Return: 0 if OK, 1 on error * * Notes: @@ -856,6 +853,8 @@ PIX *pixmt, *pixt; 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) @@ -866,8 +865,6 @@ PIX *pixmt, *pixt; return ERROR_INT("sampling factor < 1", procName, 1); if (rank < 0.0 || rank > 1.0) return ERROR_INT("rank not in [0.0 ... 1.0]", procName, 1); - if (!prval && !pgval && !pbval) - return ERROR_INT("no results requested", procName, 1); pixmt = NULL; if (pixm) { @@ -975,7 +972,7 @@ NUMA *na; * factor (subsampling factor; integer >= 1) * type (L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, * L_STANDARD_DEVIATION, L_VARIANCE) - * &value ( pixel value corresponding to input rank) + * &value ( pixel value corresponding to input type) * Return: 0 if OK, 1 on error * * Notes: @@ -1064,6 +1061,8 @@ PIXCMAP *cmap; 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); @@ -1076,8 +1075,6 @@ PIXCMAP *cmap; 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 && !pgval && !pbval) - return ERROR_INT("no values requested", procName, 1); if (prval) { if (cmap) @@ -1272,6 +1269,8 @@ PIXCMAP *cmap; 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); @@ -1282,8 +1281,6 @@ PIXCMAP *cmap; if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && type != L_STANDARD_DEVIATION) return ERROR_INT("invalid measure type", procName, 1); - if (!ppixr && !ppixg && !ppixb) - return ERROR_INT("no returned data requested", procName, 1); if (ppixr) { if (cmap) @@ -1944,7 +1941,7 @@ l_uint32 *data, *line; if (pxmax) *pxmax = 0; if (pymax) *pymax = 0; if (!pmaxval && !pxmax && !pymax) - return ERROR_INT("nothing to do", procName, 1); + return ERROR_INT("no data requested", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (pixGetColormap(pixs) != NULL) @@ -2603,7 +2600,7 @@ PIX *pixt; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!pixd || pixGetDepth(pixd) != 8) - return ERROR_INT("pixa not defined or not 8 bpp", procName, 1); + return ERROR_INT("pixd not defined or not 8 bpp", procName, 1); n = pixaGetCount(pixa); pixGetDimensions(pixd, &w, &h, NULL); if (n != w) @@ -2943,6 +2940,8 @@ PIX *pixg, *pixm; 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); @@ -3005,6 +3004,8 @@ PIX *pixg; if (pthresh) *pthresh = 0; if (pfgval) *pfgval = 0; if (pbgval) *pbgval = 0; + if (!pthresh && !pfgval && !pbgval) + return ERROR_INT("no data requested", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); diff --git a/liblept/src/pix5.c b/liblept/src/pix5.c index 5930f2f..3b627c8 100644 --- a/liblept/src/pix5.c +++ b/liblept/src/pix5.c @@ -60,6 +60,9 @@ * PIX *pixCropToSize() * PIX *pixResizeToMatch() * + * Make a frame mask + * PIX *pixMakeFrameMask() + * * Clip to foreground * PIX *pixClipToForeground() * l_int32 pixTestClipToForeground() @@ -125,10 +128,12 @@ 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); - if (!pnaw && !pnah) - return 0; n = pixaGetCount(pixa); if (pnaw) *pnaw = numaCreate(n); @@ -710,6 +715,7 @@ PIX *pixt; PROCNAME("pixFindOverlapFraction"); + if (pnoverlap) *pnoverlap = 0; if (!pratio) return ERROR_INT("&ratio not defined", procName, 1); *pratio = 0.0; @@ -977,8 +983,7 @@ PIX *pixd; PROCNAME("pixClipRectangle"); - if (pboxc) - *pboxc = NULL; + if (pboxc) *pboxc = NULL; if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!box) @@ -1234,6 +1239,74 @@ PIX *pixd; } +/*---------------------------------------------------------------------* + * Make a frame mask * + *---------------------------------------------------------------------*/ +/*! + * pixMakeFrameMask() + * + * Input: w, h (dimensions of output 1 bpp pix) + * hf1 (horizontal fraction of half-width at outer frame bdry) + * hf2 (horizontal fraction of half-width at inner frame bdry) + * vf1 (vertical fraction of half-width at outer frame bdry) + * 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 frame. + * Input fractions are in [0.0 ... 1.0]; hf1 <= hf2 and vf1 <= vf2. + * Horizontal and vertical frame widths are independently specified. + * (2) A full fg mask would have all input values 0.0. + * An empty fg mask has hf1 = vf1 = 1.0. + * A fg rectangle with no hole has hf2 == 1.0 or hv2 == 1.0. + * (3) The width of the horizontal mask parts is 2 * (vf2 - vf1) * h. + * The width of the vertical mask parts is 2 * (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 && hf2 == 0.0 && vf1 == 0.0 && vf2 == 0.0) { /* full */ + pixSetAll(pixd); + return pixd; + } + if (hf1 == 1.0 && vf1 == 1.0) { /* 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; +} + + /*---------------------------------------------------------------------* * Clip to Foreground * *---------------------------------------------------------------------*/ @@ -1262,12 +1335,10 @@ BOX *box; PROCNAME("pixClipToForeground"); + if (ppixd) *ppixd = NULL; + if (pbox) *pbox = NULL; if (!ppixd && !pbox) - return ERROR_INT("neither &pixd nor &box defined", procName, 1); - if (ppixd) - *ppixd = NULL; - if (pbox) - *pbox = NULL; + 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); @@ -1345,6 +1416,9 @@ BOX *box; * (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_int32 pixTestClipToForeground(PIX *pixs, @@ -1427,10 +1501,10 @@ BOX *boxt, *boxd; PROCNAME("pixClipBoxToForeground"); - if (!ppixd && !pboxd) - return ERROR_INT("neither &pixd nor &boxd defined", procName, 1); 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); @@ -1610,10 +1684,10 @@ BOX *boxt, *boxd; PROCNAME("pixClipBoxToEdges"); - if (!ppixd && !pboxd) - return ERROR_INT("neither &pixd nor &boxd defined", procName, 1); 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 || @@ -1705,7 +1779,7 @@ BOX *boxt, *boxd; * maxwidth (max allowed width between low and high thresh locs) * factor (sampling factor along pixel counting direction) * scanflag (direction of scan; e.g., L_FROM_LEFT) - * &loc (location in scan direction of first black pixel) + * &loc ( location in scan direction of first black pixel) * Return: 0 if OK; 1 on error or if the edge is not found * * Notes: diff --git a/liblept/src/pixabasic.c b/liblept/src/pixabasic.c index 68baef9..1448e75 100644 --- a/liblept/src/pixabasic.c +++ b/liblept/src/pixabasic.c @@ -46,7 +46,7 @@ * l_int32 pixaChangeRefcount() * PIX *pixaGetPix() * l_int32 pixaGetPixDimensions() - * PIX *pixaGetBoxa() + * BOXA *pixaGetBoxa() * l_int32 pixaGetBoxaCount() * BOX *pixaGetBox() * l_int32 pixaGetBoxGeometry() diff --git a/liblept/src/pixafunc2.c b/liblept/src/pixafunc2.c index 70d287c..067ef60 100644 --- a/liblept/src/pixafunc2.c +++ b/liblept/src/pixafunc2.c @@ -1488,7 +1488,8 @@ PIXA *pixad; * spacing (between images, and on outside) * border (width of additional black border on each image; * use 0 for no border) - * fontdir ( prints tail of filename with image) + * fontsize (to print tail of filename with image. Valid set is + * {4,6,8,10,12,14,16,18,20}. Use 0 to disable.) * outdir (subdirectory of /tmp to put N-up tiled images) * Return: 0 if OK, 1 on error * @@ -1510,7 +1511,7 @@ convertToNUpFiles(const char *dir, l_int32 tw, l_int32 spacing, l_int32 border, - const char *fontdir, + l_int32 fontsize, const char *outdir) { l_int32 d, format; @@ -1523,11 +1524,13 @@ PIXA *pixa; 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, - fontdir); + fontsize); if (!pixa) return ERROR_INT("pixa not made", procName, 1); @@ -1553,7 +1556,8 @@ PIXA *pixa; * spacing (between images, and on outside) * border (width of additional black border on each image; * use 0 for no border) - * fontdir ( prints tail of filename with image) + * 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: @@ -1567,7 +1571,7 @@ convertToNUpPixa(const char *dir, l_int32 tw, l_int32 spacing, l_int32 border, - const char *fontdir) + l_int32 fontsize) { l_int32 i, j, k, nt, n2, nout, d; char *fname, *tail; @@ -1584,13 +1588,15 @@ SARRAY *sa; 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); sa = getSortedPathnamesInDirectory(dir, substr, 0, 0); nt = sarrayGetCount(sa); n2 = nx * ny; nout = (nt + n2 - 1) / n2; pixad = pixaCreate(nout); - bmf = (fontdir) ? bmfCreate(fontdir, 6) : NULL; /* 6 pt font */ + bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize); for (i = 0, j = 0; i < nout; i++) { pixat = pixaCreate(n2); for (k = 0; k < n2 && j < nt; j++, k++) { @@ -1600,7 +1606,7 @@ SARRAY *sa; continue; } pix2 = pixScaleToSize(pix1, tw, 0); /* all images have width tw */ - if (fontdir) { + if (bmf) { splitPathAtDirectory(fname, NULL, &tail); pix3 = pixAddSingleTextline(pix2, bmf, tail, 0xff000000, L_ADD_BELOW); diff --git a/liblept/src/pixconv.c b/liblept/src/pixconv.c index 0494128..dda2685 100644 --- a/liblept/src/pixconv.c +++ b/liblept/src/pixconv.c @@ -290,9 +290,9 @@ PIX * pixRemoveColormap(PIX *pixs, l_int32 type) { -l_int32 sval, rval, gval, bval; -l_int32 i, j, k, w, h, d, wpls, wpld, opaque, ncolors, count; -l_int32 colorfound; +l_int32 sval, rval, gval, bval, val0, val1; +l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, count; +l_int32 opaque, colorfound, blackwhite; l_int32 *rmap, *gmap, *bmap, *amap, *graymap; l_uint32 *datas, *lines, *datad, *lined, *lut; l_uint32 sword, dword; @@ -327,16 +327,17 @@ PIX *pixd; type = REMOVE_CMAP_BASED_ON_SRC; } + /* Select output type depending on colormap content */ if (type == REMOVE_CMAP_BASED_ON_SRC) { - /* select output type depending on colormap */ 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) + if (d == 1 && blackwhite) /* can binarize without loss */ type = REMOVE_CMAP_TO_BINARY; else type = REMOVE_CMAP_TO_GRAYSCALE; @@ -350,7 +351,10 @@ PIX *pixd; if ((pixd = pixCopy(NULL, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixcmapGetColor(cmap, 0, &rval, &gval, &bval); - if (rval == 0) /* photometrically inverted from standard */ + 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) { @@ -1272,7 +1276,7 @@ PIX *pixd; * mingraycolors (min number of gray levels that a grayscale * image is quantized to; use 0 for default) * octlevel (for octcube quantization: 3 or 4) - * &pixd (2, 4 or 8 bpp quantized; null if too many colors) + * &pixd ( 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. * @@ -1993,7 +1997,7 @@ l_uint32 *tab, *datas, *datad, *lines, *lined; * non-cmapped 8 bpp pix, and then make a colormap and set 0 * and 1 to the desired colors. Here is an example: * pixd = pixConvert1To8(NULL, pixs, 0, 1); - * cmap = pixCreate(8); + * cmap = pixcmapCreate(8); * pixcmapAddColor(cmap, 255, 255, 255); * pixcmapAddColor(cmap, 0, 0, 0); * pixSetColormap(pixd, cmap); @@ -2403,7 +2407,7 @@ PIXCMAP *cmap; * Return: pixd (1 bpp), or null on error * * Notes: - * (1) This is a fast, quick/dirty, top-level converter. + * (1) This is a quick and dirty, top-level converter. * (2) See pixConvertTo1() for default values. */ PIX * @@ -2990,10 +2994,10 @@ pixRemoveAlpha(PIX *pixs) * (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 pixels in pixs become fully transparent, and the - * foreground pixels are fully opaque. Thus, this is a compact - * 1 bpp representation of a stencil, to paint over pixels of - * a backing image that are masked by the foreground in pixs. + * (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, @@ -3008,11 +3012,11 @@ PIXCMAP *cmap; if (pixd && (pixd != pixs)) return (PIX *)ERROR_PTR("pixd defined but != pixs", procName, NULL); - pixd = pixInvert(pixd, pixs); + pixd = pixCopy(pixd, pixs); cmap = pixcmapCreate(1); pixSetColormap(pixd, cmap); - pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* black, fully opaque */ - pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* white, fully transparent */ + pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* 0 ==> white + transparent */ + pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* 1 ==> black + opaque */ return pixd; } diff --git a/liblept/src/pngio.c b/liblept/src/pngio.c index 3ec1f6f..19f6c55 100644 --- a/liblept/src/pngio.c +++ b/liblept/src/pngio.c @@ -34,6 +34,7 @@ * l_int32 readHeaderMemPng() * l_int32 fgetPngResolution() * l_int32 isPngInterlaced() + * l_int32 fgetPngColormapInfo() * * Write png to file * l_int32 pixWritePng() [ special top level ] @@ -181,7 +182,7 @@ png_structp png_ptr; png_infop info_ptr, end_info; png_colorp palette; png_textp text_ptr; /* ptr to text_chunk */ -PIX *pix; +PIX *pix, *pixt; PIXCMAP *cmap; PROCNAME("pixReadStreamPng"); @@ -371,9 +372,9 @@ PIXCMAP *cmap; 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 < 2) - L_WARNING("num_trans = %d; should be 2\n", procName, - num_trans); + 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]; @@ -424,43 +425,36 @@ PIXCMAP *cmap; } #endif /* DEBUG_READ */ - /* If there is no colormap, PNG defines black = 0 and - * white = 1 by default for binary monochrome. Therefore, - * since we use the opposite definition, we must invert - * the image colors in either of these cases: - * (i) there is no colormap (default) - * (ii) there is a colormap which defines black to - * be 0 and white to be 1. - * We cannot use the PNG_TRANSFORM_INVERT_MONO flag - * 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.) - * If there is a colormap that defines black = 1 and - * white = 0, we don't need to do anything. - * - * How do we check the polarity of the colormap? - * The RGBA colormap determines the values of black and - * white pixels in the following way: - * if black = 1 (255), white = 0 - * 255, 255, 255, 0, 0, 0, 0, 0 - * if black = 0, white = 1 (255) - * 0, 0, 0, 0, 255, 255, 255, 0 - * So we test the first byte to see if it is 0; - * if so, invert the colors. + /* 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. * - * If there is a colormap, after inverting the pixels it is - * necessary to destroy the colormap. Otherwise, calling - * pixRemoveColormap() would invert the pixel values again! + * 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, we are rendering it as a 32 bpp, spp = 4 rgba pix. - * Do not invert the image data! + * transparency, it has already been rendered as a 32 bpp, + * spp = 4 rgba pix. */ - d = pixGetDepth(pix); - if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { -/* fprintf(stderr, "Inverting binary data on png read\n"); */ - pixInvert(pix, pix); - pixDestroyColormap(pix); + if (pixGetDepth(pix) == 1) { + if (!cmap) { + pixInvert(pix, pix); + } else { + pixt = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + pixDestroy(&pix); + pix = pixt; + } } xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); @@ -671,7 +665,7 @@ l_uint32 *pword; * * Input: stream (opened for read) * &xres, &yres ( resolution in ppi) - * Return: 0 if OK; 0 on error + * Return: 0 if OK; 1 on error * * Notes: * (1) If neither resolution field is set, this is not an error; @@ -761,6 +755,105 @@ FILE *fp; } +/* + * fgetPngColormapInfo() + * + * Input: stream (opened for read) + * &cmap (optional ; use NULL to skip) + * &transparency (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_int32 +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); + 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_png(png_ptr, info_ptr, 0, NULL); + 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 * *---------------------------------------------------------------------*/ @@ -1027,17 +1120,12 @@ char *text; if ((d != 32) && (d != 24)) { /* not rgb color */ /* Generate a temporary pix with bytes swapped. - * For a binary image, there are two conditions in - * which you must first invert the data for writing png: - * (a) no colormap - * (b) colormap with BLACK set to 0 - * png writes binary with BLACK = 0, unless contradicted - * by a colormap. If the colormap has BLACK = "1" - * (typ. about 255), do not invert the data. If there - * is no colormap, you must invert the data to store - * in default BLACK = 0 state. */ - if (d == 1 && - (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { + * 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) { pixt = pixInvert(NULL, pix); pixEndianByteSwap(pixt); } else { @@ -1195,7 +1283,8 @@ PIX *pix; return (PIX *)ERROR_PTR("stream not opened", procName, NULL); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (PIX *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(cdata, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -1245,7 +1334,8 @@ FILE *fp; ret = pixWriteStreamPng(fp, pix, gamma); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); ret = pixWriteStreamPng(fp, pix, gamma); rewind(fp); *pdata = l_binaryReadStream(fp, psize); diff --git a/liblept/src/pngiostub.c b/liblept/src/pngiostub.c index 54572d8..8c2520e 100644 --- a/liblept/src/pngiostub.c +++ b/liblept/src/pngiostub.c @@ -86,6 +86,13 @@ l_int32 isPngInterlaced(const char *filename, l_int32 *pinterlaced) /* ----------------------------------------------------------------------*/ +l_int32 fgetPngColormapInfo(FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency) +{ + return ERROR_INT("function not present", "fgetPngColormapInfo", 1); +} + +/* ----------------------------------------------------------------------*/ + l_int32 pixWritePng(const char *filename, PIX *pix, l_float32 gamma) { return ERROR_INT("function not present", "pixWritePng", 1); diff --git a/liblept/src/pnmio.c b/liblept/src/pnmio.c index f56bfb1..3b4b931 100644 --- a/liblept/src/pnmio.c +++ b/liblept/src/pnmio.c @@ -41,6 +41,7 @@ * * Local helpers * static l_int32 pnmReadNextAsciiValue(); + * static l_int32 pnmReadNextNumber(); * static l_int32 pnmSkipCommentLines(); * * These are here by popular demand, with the help of Mattias @@ -84,6 +85,7 @@ */ #include +#include #include "allheaders.h" /* --------------------------------------------*/ @@ -92,6 +94,7 @@ static l_int32 pnmReadNextAsciiValue(FILE *fp, l_int32 *pval); +static l_int32 pnmReadNextNumber(FILE *fp, l_int32 *pval); static l_int32 pnmSkipCommentLines(FILE *fp); /* a sanity check on the size read from file */ @@ -279,6 +282,8 @@ freadHeaderPnm(FILE *fp, { l_int32 w, h, d, type; l_int32 maxval; +char whitespace; +char buf[64]; PROCNAME("freadHeaderPnm"); @@ -301,14 +306,24 @@ l_int32 maxval; 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) - return ERROR_INT("invalid size", 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 */ + /* 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; } else if (type == 2 || type == 5) { - if (fscanf(fp, "%d\n", &maxval) != 1) + if (pnmReadNextNumber(fp, &maxval)) return ERROR_INT("invalid read for maxval (2,5)", procName, 1); if (maxval == 3) { d = 2; @@ -323,7 +338,7 @@ l_int32 maxval; return ERROR_INT("invalid maxval", procName, 1); } } else { /* type == 3 || type == 6; this is rgb */ - if (fscanf(fp, "%d\n", &maxval) != 1) + if (pnmReadNextNumber(fp, &maxval)) return ERROR_INT("invalid read for maxval (3,6)", procName, 1); if (maxval != 255) L_WARNING("unexpected maxval = %d\n", procName, maxval); @@ -512,8 +527,10 @@ PIX *pixs; fputc('1', fp); fputc(' ', fp); count += 2; - if (count >= 70) + if (count >= 70) { fputc('\n', fp); + count = 0; + } } } } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ @@ -615,7 +632,8 @@ PIX *pix; return (PIX *)ERROR_PTR("stream not opened", procName, NULL); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return (PIX *)ERROR_PTR("tmpfile stream not opened", procName, NULL); fwrite(cdata, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -662,7 +680,8 @@ FILE *fp; return ERROR_INT("stream not opened", procName, 1); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); fwrite(cdata, 1, size, fp); rewind(fp); #endif /* HAVE_FMEMOPEN */ @@ -711,7 +730,8 @@ FILE *fp; ret = pixWriteStreamPnm(fp, pix); #else L_WARNING("work-around: writing to a temp file\n", procName); - fp = tmpfile(); + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); ret = pixWriteStreamPnm(fp, pix); rewind(fp); *pdata = l_binaryReadStream(fp, psize); @@ -756,6 +776,57 @@ l_int32 c, ignore; } +/*! + * pnmReadNextNumber() + * + * Input: file stream + * &val ( 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 + * preceeds the binary data. + */ +static l_int32 +pnmReadNextNumber(FILE *fp, + l_int32 *pval) +{ +char buf[8]; +l_int32 i, c, foundws, ignore; + + 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++) { + 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; +} + + /*! * pnmSkipCommentLines() * diff --git a/liblept/src/psio2.c b/liblept/src/psio2.c index f073d87..1add44e 100644 --- a/liblept/src/psio2.c +++ b/liblept/src/psio2.c @@ -79,11 +79,6 @@ * l_int32 getResLetterPage() * l_int32 getResA4Page() * - * Utility for encoding and decoding data with ascii85 - * char *encodeAscii85() - * static l_int32 *convertChunkToAscii85() - * l_uint8 *decodeAscii85() - * * Setting flag for writing bounding box hint * void l_psWriteBoundingBox() * @@ -100,12 +95,10 @@ /* Set default for writing bounding box hint */ static l_int32 var_PS_WRITE_BOUNDING_BOX = 1; -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; static const l_int32 DEFAULT_INPUT_RES = 300; /* typical scan res, ppi */ static const l_int32 MIN_RES = 5; static const l_int32 MAX_RES = 3000; -static const l_int32 MAX_85_LINE_COUNT = 64; /* max line length ascii85 */ /* For computing resolution that fills page to desired amount */ static const l_int32 LETTER_WIDTH = 612; /* points */ @@ -114,16 +107,6 @@ static const l_int32 A4_WIDTH = 595; /* points */ static const l_int32 A4_HEIGHT = 842; /* points */ static const l_float32 DEFAULT_FILL_FRACTION = 0.95; -static const l_uint32 power85[5] = {1, - 85, - 85 * 85, - 85 * 85 * 85, - 85 * 85 * 85 * 85}; - -static l_int32 convertChunkToAscii85(l_uint8 *inarray, l_int32 insize, - l_int32 *pindex, char *outbuf, - l_int32 *pnbout); - #ifndef NO_CONSOLE_IO #define DEBUG_JPEG 0 #define DEBUG_G4 0 @@ -1968,237 +1951,6 @@ l_int32 resw, resh, res; } - -/*-------------------------------------------------------------* - * Utility for encoding and decoding data with ascii85 * - *-------------------------------------------------------------*/ -/*! - * encodeAscii85() - * - * Input: inarray (input data) - * insize (number of bytes in input array) - * &outsize ( 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(l_uint8 *inarray, - l_int32 insize, - l_int32 *poutsize) -{ -char *chara; -char *outbuf; -l_int32 maxsize, i, index, outindex, linecount, nbout, eof; - - PROCNAME("encodeAscii85"); - - if (!inarray) - return (char *)ERROR_PTR("inarray not defined", procName, NULL); - - /* Accumulate results in char array */ - maxsize = (l_int32)(80. + (insize * 5. / 4.) * - (1. + 2. / MAX_85_LINE_COUNT)); - if ((chara = (char *)CALLOC(maxsize, sizeof(char))) == NULL) - return (char *)ERROR_PTR("chara not made", procName, NULL); - - if ((outbuf = (char *)CALLOC(8, sizeof(char))) == NULL) - return (char *)ERROR_PTR("outbuf 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_85_LINE_COUNT) { - chara[outindex++] = '\n'; - linecount = 0; - } - } - if (eof == TRUE) { - if (linecount != 0) - chara[outindex++] = '\n'; - chara[outindex++] = '~'; - chara[outindex++] = '>'; - chara[outindex++] = '\n'; - break; - } - } - - FREE(outbuf); - *poutsize = outindex; - return chara; -} - - -/*! - * convertChunkToAscii85() - * - * Input: inarray (input data) - * insize (number of bytes in input array) - * &index (use and -- ptr) - * outbuf (holds 8 ascii chars; we use no more than 7) - * &nbsout ( 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(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; -} - - -/*! - * decodeAscii85() - * - * Input: inarray (ascii85 input data) - * insize (number of bytes in input array) - * &outsize ( 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(char *ina, - l_int32 insize, - l_int32 *poutsize) -{ -char inc; -char *pin; -l_uint8 val; -l_uint8 *outa; -l_int32 maxsize, ocount, bytecount, index; -l_uint32 oword; - - PROCNAME("decodeAscii85"); - - if (!ina) - return (l_uint8 *)ERROR_PTR("ina not defined", procName, NULL); - - /* Accumulate results in outa */ - maxsize = (l_int32)(80. + (insize * 4. / 5.)); /* plenty big */ - if ((outa = (l_uint8 *)CALLOC(maxsize, sizeof(l_uint8))) == NULL) - return (l_uint8 *)ERROR_PTR("outa not made", procName, NULL); - - pin = ina; - 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 */ - fprintf(stderr, " %d extra bytes output\n", bytecount - 1); - switch (bytecount) { - case 0: /* normal eof */ - case 1: /* error */ - break; - case 2: /* 1 extra byte */ - oword = oword * (85 * 85 * 85) + 0xffffff; - outa[ocount] = (oword >> 24) & 0xff; - break; - case 3: /* 2 extra bytes */ - oword = oword * (85 * 85) + 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; -} - - /*-------------------------------------------------------------* * Setting flag for writing bounding box hint * *-------------------------------------------------------------*/ diff --git a/liblept/src/psio2stub.c b/liblept/src/psio2stub.c index a2e11b5..a993f16 100644 --- a/liblept/src/psio2stub.c +++ b/liblept/src/psio2stub.c @@ -225,20 +225,6 @@ l_int32 getResA4Page(l_int32 w, l_int32 h, l_float32 fillfract) /* ----------------------------------------------------------------------*/ -char * encodeAscii85(l_uint8 *inarray, l_int32 insize, l_int32 *poutsize) -{ - return (char *)ERROR_PTR("function not present", "encodeAscii85", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_uint8 * decodeAscii85(char *ina, l_int32 insize, l_int32 *poutsize) -{ - return (l_uint8 *)ERROR_PTR("function not present", "decodeAscii85", NULL); -} - -/* ----------------------------------------------------------------------*/ - void l_psWriteBoundingBox(l_int32 flag) { L_ERROR("function not present\n", "l_psWriteBoundingBox"); diff --git a/liblept/src/readfile.c b/liblept/src/readfile.c index f96afa5..ea84582 100644 --- a/liblept/src/readfile.c +++ b/liblept/src/readfile.c @@ -24,7 +24,6 @@ - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ - /* * readfile.c: reads image on file into memory * @@ -63,24 +62,14 @@ * tiff (including most varieties of compression) * gif * webp - * (3) This file format is recognized by the library but reading - * is not supported: - * jp2 (jpeg2000) - * (4) All other file types will get an "unknown format" error. + * jp2 (jpeg 2000) + * (3) Other file types will get an "unknown format" error. */ #include #include "allheaders.h" - /* choose type of PIX to be generated */ -enum { - READ_24_BIT_COLOR = 0, /* read in as 24 (really 32) bit pix */ - CONVERT_TO_PALETTE = 1, /* convert to 8 bit colormapped pix */ - READ_GRAY = 2 /* read gray only */ -}; - - /* Output files for ioFormatTest(). - * Note that the test for jpeg is not yet implemented */ + /* Output files for ioFormatTest(). */ static const char *FILE_BMP = "/tmp/lept/format.bmp"; static const char *FILE_PNG = "/tmp/lept/format.png"; static const char *FILE_PNM = "/tmp/lept/format.pnm"; @@ -92,14 +81,16 @@ static const char *FILE_LZW = "/tmp/lept/format_lzw.tif"; static const char *FILE_ZIP = "/tmp/lept/format_zip.tif"; static const char *FILE_TIFF = "/tmp/lept/format.tif"; static const char *FILE_JPG = "/tmp/lept/format.jpg"; +static const char *FILE_GIF = "/tmp/lept/format.gif"; +static const char *FILE_WEBP = "/tmp/lept/format.webp"; +static const char *FILE_JP2K = "/tmp/lept/format.jp2"; - /* I found these from the source code to the unix file */ - /* command. man 1 file */ 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 * *---------------------------------------------------------------------*/ @@ -200,15 +191,7 @@ PIX *pix; fclose(fp); return (PIX *)ERROR_PTR("pix not read", procName, NULL); } - - /* Close the stream except if GIF under windows, because - * DGifCloseFile() closes the windows file stream! */ - if (pixGetInputFormat(pix) != IFF_GIF) - fclose(fp); -#ifndef _WIN32 - else /* gif file */ - fclose(fp); -#endif /* ! _WIN32 */ + fclose(fp); return pix; } @@ -336,8 +319,7 @@ PIX *pix; break; case IFF_JFIF_JPEG: - if ((pix = pixReadStreamJpeg(fp, READ_24_BIT_COLOR, 1, NULL, hint)) - == NULL) + 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) @@ -372,7 +354,7 @@ PIX *pix; break; case IFF_JP2: - if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0)) == NULL) + if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0, 0)) == NULL) return (PIX *)ERROR_PTR("jp2: no pix returned", procName, NULL); break; @@ -775,7 +757,7 @@ l_int32 format; * (2) On windows, this only reads tiff formatted files directly from * memory. For other formats, it write to a temp file and * decompress from file. - * (3) findFileFormatBuffer() requires up to 8 bytes to decide on + * (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. @@ -791,8 +773,8 @@ PIX *pix; if (!data) return (PIX *)ERROR_PTR("data not defined", procName, NULL); - if (size < 8) - return (PIX *)ERROR_PTR("size < 8", procName, NULL); + if (size < 12) + return (PIX *)ERROR_PTR("size < 12", procName, NULL); pix = NULL; findFileFormatBuffer(data, &format); @@ -804,8 +786,7 @@ PIX *pix; break; case IFF_JFIF_JPEG: - if ((pix = pixReadMemJpeg(data, size, READ_24_BIT_COLOR, 1, NULL, 0)) - == NULL) + if ((pix = pixReadMemJpeg(data, size, 0, 1, NULL, 0)) == NULL) return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL); break; @@ -837,7 +818,7 @@ PIX *pix; break; case IFF_JP2: - if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0)) == NULL) + if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0, 0)) == NULL) return (PIX *)ERROR_PTR("jp2k: no pix returned", procName, NULL); break; @@ -1037,17 +1018,29 @@ PIX *pix; l_int32 ioFormatTest(const char *filename) { -l_int32 d, equal, problems; -PIX *pixs, *pixc, *pix1, *pix2; -PIXCMAP *cmap; +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); - if ((pixs = pixRead(filename)) == NULL) - return ERROR_INT("pixs not made", 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"); @@ -1233,6 +1226,101 @@ PIXCMAP *cmap; 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); @@ -1240,3 +1328,4 @@ PIXCMAP *cmap; pixDestroy(&pixs); return problems; } + diff --git a/liblept/src/recogbasic.c b/liblept/src/recogbasic.c index fa8686a..188222e 100644 --- a/liblept/src/recogbasic.c +++ b/liblept/src/recogbasic.c @@ -96,7 +96,7 @@ * For training numeric input, an example set of calls that scales * each training input to (w, h) and will use the averaged * templates for identifying unknown characters is: - * L_Recog *rec = recogCreate(w, h, L_USE_AVERAGE, 128, 1, "fonts"); + * L_Recog *rec = recogCreate(w, h, L_USE_AVERAGE, 128, 1); * for (i = 0; i < n; i++) { // read in n training digits * Pix *pix = ... * recogTrainLabelled(rec, pix, NULL, text[i], 0, 0); @@ -118,7 +118,7 @@ * * If using all examples for identification, all scaled to (w, h), * and with outliers removed, do something like this: - * L_Recog *rec = recogCreate(w, h, L_USE_ALL, 128, 1, "fonts"); + * L_Recog *rec = recogCreate(w, h, L_USE_ALL, 128, 1); * for (i = 0; i < n; i++) { // read in n training characters * Pix *pix = ... * recogTrainLabelled(rec, pix, NULL, text[i], 0, 0); @@ -131,7 +131,7 @@ * pix is the character string: * * L_Recog *recboot = recogCreateFromPixa(pixa, w, h, L_USE_AVERAGE, - * 128, 1, "fonts"); + * 128, 1); * * This is useful as a "bootstrap" recognizer for training a new * recognizer (rec) on an unlabelled data set that has a different @@ -141,7 +141,7 @@ * from a single source, like a book), call recogSetScaling() to * regenerate all the scaled samples and averages: * - * L_Recog *rec = recogCreate(w, h, L_USE_ALL, 128, 1, "fonts"); + * L_Recog *rec = recogCreate(w, h, L_USE_ALL, 128, 1); * for (i = 0; i < n; i++) { // read in n training characters * Pix *pix = ... * recogTrainUnlabelled(rec, recboot, pix, NULL, 1, 0.75, 0); @@ -206,7 +206,6 @@ L_RECOGA *recoga; * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) - * fontdir ( directory for bitmap fonts for debugging) * Return: recoga, or null on error * * Notes: @@ -225,8 +224,7 @@ recogaCreateFromPixaa(PIXAA *paa, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, - l_int32 maxyshift, - const char *fontdir) + l_int32 maxyshift) { l_int32 n, i, full; L_RECOG *recog; @@ -248,7 +246,7 @@ PIXA *pixa; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); recog = recogCreateFromPixa(pixa, scalew, scaleh, templ_type, - threshold, maxyshift, fontdir); + threshold, maxyshift); recogaAddRecog(recoga, recog); pixaDestroy(&pixa); } @@ -575,7 +573,6 @@ recogSetBootflag(L_RECOG *recog) * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) - * fontdir ( directory for bitmap fonts for debugging) * Return: recd, or null on error * * Notes: @@ -588,8 +585,7 @@ recogCreateFromRecog(L_RECOG *recs, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, - l_int32 maxyshift, - const char *fontdir) + l_int32 maxyshift) { L_RECOG *recd; PIXA *pixa; @@ -601,7 +597,7 @@ PIXA *pixa; pixa = pixaaFlattenToPixa(recs->pixaa_u, NULL, L_CLONE); recd = recogCreateFromPixa(pixa, scalew, scaleh, templ_type, threshold, - maxyshift, fontdir); + maxyshift); pixaDestroy(&pixa); return recd; } @@ -616,7 +612,6 @@ PIXA *pixa; * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) - * fontdir ( directory for bitmap fonts for debugging) * Return: recog, or null on error * * Notes: @@ -634,8 +629,7 @@ recogCreateFromPixa(PIXA *pixa, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, - l_int32 maxyshift, - const char *fontdir) + l_int32 maxyshift) { char *text; l_int32 full, n, i, ntext; @@ -661,7 +655,7 @@ PIX *pix; L_ERROR("%d text strings < %d pix\n", procName, ntext, n); recog = recogCreate(scalew, scaleh, templ_type, threshold, - maxyshift, fontdir); + maxyshift); if (!recog) return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); for (i = 0; i < n; i++) { @@ -689,7 +683,6 @@ PIX *pix; * templ_type (L_USE_AVERAGE or L_USE_ALL) * threshold (for binarization; typically ~128) * maxyshift (from nominal centroid alignment; typically 0 or 1) - * fontdir ( directory for bitmap fonts for debugging) * Return: recog, or null on error * * Notes: @@ -705,8 +698,7 @@ recogCreate(l_int32 scalew, l_int32 scaleh, l_int32 templ_type, l_int32 threshold, - l_int32 maxyshift, - const char *fontdir) + l_int32 maxyshift) { L_RECOG *recog; PIXA *pixa; @@ -730,11 +722,8 @@ PIXAA *paa; recog->maxyshift = maxyshift; recog->asperity_fr = DEFAULT_ASPERITY_FRACT; recogSetPadParams(recog, NULL, NULL, NULL, -1, -1, -1); - if (fontdir) { - recog->fontdir = stringNew(fontdir); - recog->bmf = bmfCreate(fontdir, 6); - recog->bmf_size = 6; - } + recog->bmf = bmfCreate(NULL, 6); + recog->bmf_size = 6; recog->maxarraysize = MAX_EXAMPLES_IN_CLASS; recog->index = -1; @@ -819,7 +808,6 @@ L_RECOG *recog; pixDestroy(&recog->pixdb_range); pixaDestroy(&recog->pixadb_boot); pixaDestroy(&recog->pixadb_split); - FREE(recog->fontdir); bmfDestroy(&recog->bmf); rchDestroy(&recog->rch); rchaDestroy(&recog->rcha); @@ -1127,7 +1115,7 @@ L_RECOGA *recoga; if (!fp) return (L_RECOGA *)ERROR_PTR("stream not defined", procName, NULL); - if (fscanf(fp, "\nRecog Version %d\n", &version) != 1) + if (fscanf(fp, "\nRecoga Version %d\n", &version) != 1) return (L_RECOGA *)ERROR_PTR("not a recog file", procName, NULL); if (version != RECOG_VERSION_NUMBER) return (L_RECOGA *)ERROR_PTR("invalid recog version", procName, NULL); @@ -1203,7 +1191,7 @@ L_RECOG *recog; if (!recoga) return ERROR_INT("recoga not defined", procName, 1); - fprintf(fp, "\nRecog Version %d\n", RECOG_VERSION_NUMBER); + fprintf(fp, "\nRecoga Version %d\n", RECOG_VERSION_NUMBER); fprintf(fp, "Number of recognizers = %d\n\n", recoga->n); for (i = 0; i < recoga->n; i++) { @@ -1339,7 +1327,7 @@ SARRAY *sa_text; if (fscanf(fp, "Scale to height = %d\n", &scaleh) != 1) return (L_RECOG *)ERROR_PTR("height not read", procName, NULL); if ((recog = recogCreate(scalew, scaleh, templ_type, threshold, - maxyshift, NULL)) == NULL) + maxyshift)) == NULL) return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); if (fscanf(fp, "Serialized filename: %s\n", fname) != 1) diff --git a/liblept/src/recogdid.c b/liblept/src/recogdid.c index 64ac613..4ff4f58 100644 --- a/liblept/src/recogdid.c +++ b/liblept/src/recogdid.c @@ -438,6 +438,7 @@ L_RDID *did; PROCNAME("recogRunViterbi"); + if (ppixdb) *ppixdb = NULL; if (!recog) return ERROR_INT("recog not defined", procName, 1); if ((did = recogGetDid(recog)) == NULL) @@ -560,6 +561,7 @@ L_RDID *did; PROCNAME("recogRescoreDidResult"); + if (ppixdb) *ppixdb = NULL; if (!recog) return ERROR_INT("recog not defined", procName, 1); if ((did = recogGetDid(recog)) == NULL) @@ -627,13 +629,8 @@ L_RDID *did; return (PIX *)ERROR_PTR("recog not defined", procName, NULL); if ((did = recogGetDid(recog)) == NULL) return (PIX *)ERROR_PTR("did not defined", procName, NULL); - if (recog->fontdir == NULL) { - L_WARNING("no bitmap fonts available\n", procName); - bmf = NULL; - } else { - bmf = bmfCreate(recog->fontdir, 8); - } + bmf = bmfCreate(NULL, 8); pixs = pixScale(did->pixs, 4.0, 4.0); pix0 = pixAddBorderGeneral(pixs, 0, 0, 0, 40, 0); pix1 = pixConvertTo32(pix0); @@ -881,12 +878,10 @@ L_RDID *did; PROCNAME("recogGetWindowedArea"); - if (!pwsum) - return ERROR_INT("&wsum not defined", procName, 1); - *pwsum = 0; - if (!pdely) - return ERROR_INT("&dely not defined", procName, 1); - *pdely = 0; + 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) @@ -1001,5 +996,3 @@ L_RCH *rch; return 0; } - - diff --git a/liblept/src/recogident.c b/liblept/src/recogident.c index 9fe2f3e..3db8b36 100644 --- a/liblept/src/recogident.c +++ b/liblept/src/recogident.c @@ -143,24 +143,23 @@ static l_int32 recogaTransferRch(L_RECOGA *recoga, L_RECOG *recog, * &pixa ( images of identified components) * &pixdb ( debug pix: inputs and best fits) * debugsplit (1 returns pix split debugging images) - * Return: 0 if OK; 1 if more or less than nitems were found (a warning); - * 2 on error. + * Return: 0 if OK; 1 if nothing is found; 2 for other errors. + * (Get a warning if nitems and the number found are both > 0, + * but not equal to each other.) * * Notes: * (1) This filters the input pixa, looking for @nitems if requested. * Set @nitems == 0 if you don't know how many chars to expect. * (2) This bundles the filtered components into a pixa and calls * recogIdentifyPixa(). If @nitems > 0, use @minw = -1 and - * @minh = -1 to remove all noise components. If @nitems > 0 - * and it doesn't agree with the number of filtered components - * in pixs, a warning is issued and a 1 is returned. + * @minh = -1 to remove all noise components. * (3) Set @minw = 0 and @minh = 0 to get all noise components. * Set @minw > 0 and/or @minh > 0 to retain selected noise components. * All noise components are recognized as an empty string with * a score of 0.0. - * (4) An attempt is made to return 2-dimensional sorted arrays - * of (optional) images and boxes, which can then be used to - * aggregate identified characters into numbers or words. + * (4) 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. */ @@ -175,7 +174,7 @@ recogaIdentifyMultiple(L_RECOGA *recoga, PIX **ppixdb, l_int32 debugsplit) { -l_int32 n, done, ret; +l_int32 n, done; BOXA *boxa; PIX *pixb; PIXA *pixa; @@ -203,8 +202,8 @@ L_RECOG *recog; pixb = pixClone(pixs); /* Noise removal and splitting of touching characters */ - recogSplitIntoCharacters(recog, pixb, minw, minh, &boxa, &pixa, &naid, - debugsplit); + recogSplitIntoCharacters(recog, pixb, minw, minh, &boxa, &pixa, + &naid, debugsplit); pixDestroy(&pixb); if (!pixa || (n = pixaGetCount(pixa)) == 0) { pixaDestroy(&pixa); @@ -214,11 +213,8 @@ L_RECOG *recog; return 1; } - ret = 0; - if (nitems > 0 && n != nitems) { + if (nitems > 0 && n != nitems) L_WARNING("Expected %d items; found %d\n", procName, nitems, n); - ret = 1; - } recogaIdentifyPixa(recoga, pixa, naid, ppixdb); if (pboxa) @@ -231,7 +227,7 @@ L_RECOG *recog; pixaDestroy(&pixa); numaDestroy(&naid); - return ret; + return 0; } @@ -248,8 +244,7 @@ L_RECOG *recog; * &pixa ( character images) * &naid ( indices of components to identify) * debug (1 for results written to pixadb_split) - * - * Return: 0 if OK, 1 on error + * 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 @@ -295,11 +290,11 @@ PIX *pix, *pix1, *pix2; PROCNAME("recogSplitIntoCharacters"); + if (pboxa) *pboxa = NULL; + if (ppixa) *ppixa = NULL; + if (pnaid) *pnaid = NULL; if (!pboxa || !ppixa || !pnaid) return ERROR_INT("&boxa, &pixa and &naid not defined", procName, 1); - *pboxa = NULL; - *ppixa = NULL; - *pnaid = NULL; if (!recog) return ERROR_INT("recog not defined", procName, 1); if (!recog->train_done) @@ -307,7 +302,7 @@ PIX *pix, *pix1, *pix2; if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); pixZero(pixs, &empty); - if (empty) return 0; + if (empty) return 1; /* Small vertical close for consolidation. Don't do a horizontal * closing, because it might join separate characters. */ @@ -341,7 +336,7 @@ PIX *pix, *pix1, *pix2; boxaDestroy(&boxa1); boxaDestroy(&boxa3); L_WARNING("all components removed\n", procName); - return 0; + return 1; } /* Save everything and split the large non-noise components */ @@ -357,12 +352,16 @@ PIX *pix, *pix1, *pix2; pix = pixClipRectangle(pixs, box, NULL); recogCorrelationBestRow(recog, pix, &boxat1, NULL, NULL, NULL, debug); - boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0); - boxaJoin(boxa2, boxat2, 0, -1); - boxaDestroy(&boxat1); - boxaDestroy(&boxat2); pixDestroy(&pix); boxDestroy(&box); + if (!boxat1) { + L_ERROR("boxat1 not found for component %d\n", procName, i); + } else { + boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0); + boxaJoin(boxa2, boxat2, 0, -1); + boxaDestroy(&boxat1); + boxaDestroy(&boxat2); + } } } boxaDestroy(&boxa1); @@ -467,15 +466,15 @@ l_int32 iter; 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 (!pboxa) - return ERROR_INT("&boxa not defined", procName, 1); - *pboxa = NULL; if (!recog->train_done) return ERROR_INT("training not finished", procName, 1); @@ -641,15 +640,13 @@ PIX *pix1, *pix2; PROCNAME("recogCorrelationBestChar"); - if (ppixdb) *ppixdb = NULL; if (pindex) *pindex = 0; if (pcharstr) *pcharstr = NULL; - if (!pbox) - return ERROR_INT("&box not defined", procName, 1); - *pbox = NULL; - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; + 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) @@ -987,7 +984,7 @@ recogIdentifyPixa(L_RECOG *recog, PIX **ppixdb) { char *text; -l_int32 i, n, doit, index, depth; +l_int32 i, n, doit, fail, index, depth; l_float32 score; NUMA *naidt; PIX *pix1, *pix2, *pix3; @@ -1020,15 +1017,21 @@ L_RCH *rch; depth = 1; for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = NULL; + fail = FALSE; numaGetIValue(naidt, i, &doit); if (!doit) recogSkipIdentify(recog); else if (!ppixdb) - recogIdentifyPix(recog, pix1, NULL); + fail = recogIdentifyPix(recog, pix1, NULL); else - recogIdentifyPix(recog, pix1, &pix2); + 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); @@ -1104,7 +1107,8 @@ PTA *pta; recogAverageSamples(recog, 0); /* Binarize and crop to foreground if necessary */ - pix0 = recogProcessToIdentify(recog, pixs, 0); + if ((pix0 = recogProcessToIdentify(recog, pixs, 0)) == NULL) + return ERROR_INT("no fg pixels in pix0", procName, 1); /* Do correlation at all positions within +-maxyshift of * the nominal centroid alignment. */ @@ -1624,7 +1628,8 @@ L_RCHA *rchas, *rchad; * Input: recog (with LUT's pre-computed) * pixs (typ. single character, possibly d > 1 and uncropped) * pad (extra pixels added to left and right sides) - * Return: pixd (1 bpp, clipped to foreground), or null on error. + * 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 @@ -1646,7 +1651,7 @@ PIX *pix1, *pix2, *pixd; if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) > 1) + if (pixGetDepth(pixs) != 1) pix1 = pixThresholdToBinary(pixs, recog->threshold); else pix1 = pixClone(pixs); @@ -1655,8 +1660,11 @@ PIX *pix1, *pix2, *pixd; pixClipToForeground(pix1, &pix2, NULL); else pix2 = pixClone(pix1); - pixd = pixAddBorderGeneral(pix2, pad, pad, 0, 0, 0); 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; } @@ -1832,7 +1840,7 @@ l_float32 aspratio, fract; /*! * recogaExtractNumbers() * - * Input: recog + * Input: recoga * boxas (location of components) * scorethresh (min score for which we accept a component) * spacethresh (max horizontal distance allowed between digits, @@ -1842,21 +1850,24 @@ l_float32 aspratio, fract; * Return: sa (of identified numbers), or null on error * * Notes: - * (1) Each string in the returned sa contains a sequence of ascii + * (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. - * (2) The horizontal distance between boxes (limited by @spacethresh) + * (3) The horizontal distance between boxes (limited by @spacethresh) * is the negative of the horizontal overlap. - * (3) We allow two digits to be combined if these conditions apply: + * (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 - * (4) Each numa in the optionally returned naa contains the digit + * (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. - * (5) 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. */ SARRAY * recogaExtractNumbers(L_RECOGA *recoga, @@ -1894,6 +1905,8 @@ SARRAY *satext, *sa, *saout; } rchaExtract(recoga->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); } @@ -1956,6 +1969,13 @@ SARRAY *satext, *sa, *saout; numaDestroy(&nascore); sarrayDestroy(&satext); + if (sarrayGetCount(saout) == 0) { + sarrayDestroy(&saout); + boxaaDestroy(&baa); + numaaDestroy(&naa); + return (SARRAY *)ERROR_PTR("saout has no strings", procName, NULL); + } + if (pbaa) *pbaa = baa; else diff --git a/liblept/src/recogtrain.c b/liblept/src/recogtrain.c index 0dfc99f..3ef255b 100644 --- a/liblept/src/recogtrain.c +++ b/liblept/src/recogtrain.c @@ -62,7 +62,7 @@ * l_int32 recogShowAverageTemplates() * PIX *recogShowMatchesInRange() * PIX *recogShowMatch() - * l_int32 recogMakeBmf() + * l_int32 recogResetBmf() * * Static helpers * static char *l_charToString() @@ -283,7 +283,7 @@ PIX *pixc, *pixb, *pixt, *pix1, *pix2; * pixs (if depth > 1, will be thresholded to 1 bpp) * box ( cropping box) * text ( if null, use text field in pix) - * &pixa (one pix, 1 bpp, labelled) + * &pixa ( one pix, 1 bpp, labelled) * Return: 0 if OK, 1 on error * * Notes: @@ -454,7 +454,7 @@ l_int32 w, h; PROCNAME("recogScaleCharacter"); if (!recog) - return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); @@ -629,11 +629,11 @@ 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 (px) *px = 0; - if (py) *py = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); @@ -971,7 +971,7 @@ PTA *pta, *pta_u; * recogaTrainingDone() * * Input: recoga - * &done (1 if training finished on all recog; 0 otherwise) + * &done ( 1 if training finished on all recog; else 0) * Return: 0 if OK, 1 on error */ l_int32 @@ -1180,7 +1180,7 @@ recogPadTrainingSet(L_RECOG **precog, l_int32 debug) { const char *bootdir, *bootpattern, *bootpath; -char *boottext, *fontdir; +char *boottext; l_int32 i, k, min_nopad, npix, nclass, nboot, nsamp, nextra, ntoadd; l_int32 ave_height, targeth, setid, index, allclasses; l_int32 *lut; @@ -1224,19 +1224,16 @@ L_RECOGA *recoga; * input recog and return a generic boot recognizer that will * be run using scaling both width and height. * ----------------------------------------------------------*/ - fontdir = (recog->fontdir) ? stringNew(recog->fontdir) : NULL; if (recog->samplenum < MIN_TOTAL_SAMPLES) { L_WARNING("too few samples in recog; using bootrecog only\n", procName); bootpath = recog->bootpath; L_INFO("boot path = %s\n", procName, bootpath); if ((pixaboot = pixaRead(bootpath)) == NULL) return ERROR_INT("pixaboot not read", procName, 1); - rec1 = recogCreateFromPixa(pixaboot, 20, 32, L_USE_AVERAGE, 100, - 1, fontdir); + rec1 = recogCreateFromPixa(pixaboot, 20, 32, L_USE_AVERAGE, 100, 1); recogReplaceInRecoga(&recog, rec1); /* destroys recog */ *precog = rec1; pixaDestroy(&pixaboot); - FREE(fontdir); return 0; } @@ -1262,8 +1259,7 @@ L_RECOGA *recoga; * averages from a copy of the input recog, also scaled to h = 32. */ if ((paa1 = pixaaReadFromFiles(bootdir, bootpattern, 0, 0)) == NULL) return ERROR_INT("boot recog files not found", procName, 1); - recoga = recogaCreateFromPixaa(paa1, 0, 32, L_USE_AVERAGE, 100, 1, - fontdir); + recoga = recogaCreateFromPixaa(paa1, 0, 32, L_USE_AVERAGE, 100, 1); pixaaDestroy(&paa1); if (!recoga) return ERROR_INT("recoga not made", procName, 1); @@ -1271,12 +1267,10 @@ L_RECOGA *recoga; /* The parameters of the input recog must match those of the * boot array. Replace the input recog with a new one, that * uses the average templates for matching, scaled to h = 32. */ - rec1 = recogCreateFromRecog(recog, 0, 32, L_USE_AVERAGE, 100, 1, - fontdir); + rec1 = recogCreateFromRecog(recog, 0, 32, L_USE_AVERAGE, 100, 1); recogReplaceInRecoga(&recog, rec1); /* destroys recog */ *precog = rec1; recog = rec1; - FREE(fontdir); /* Now for each class in the recog, decide which recog in recoga * should be used to select samples for padding the recog. */ @@ -1475,10 +1469,10 @@ PIXA *pixa; PROCNAME("recogAverageClassGeom"); - if (!pnaw && !pnah) - return ERROR_INT("nothing to do", procName, 1); if (pnaw) *pnaw = NULL; if (pnah) *pnah = NULL; + if (!pnaw && !pnah) + return ERROR_INT("no output requested", procName, 1); if (!recog) return ERROR_INT("recog not defined", procName, 1); @@ -1561,18 +1555,14 @@ L_RECOG *rec; PROCNAME("recogBestCorrelForPadding"); - if (!pnaset) - return ERROR_INT("&naset not defined", procName, 1); - *pnaset = NULL; - if (!pnaindex) - return ERROR_INT("&naindex not defined", procName, 1); - *pnaindex = NULL; - if (!pnascore) - return ERROR_INT("&nascore not defined", procName, 1); - *pnascore = NULL; - if (!pnasum) - return ERROR_INT("&nasum not defined", procName, 1); - *pnasum = NULL; + if (ppixadb) *ppixadb = NULL; + if (pnaset) *pnaset = NULL; + if (pnaindex) *pnaindex = NULL; + if (pnascore) *pnascore = NULL; + if (pnasum) *pnasum = NULL; + if (!pnaset || !pnaindex || !pnascore || !pnasum) + return ERROR_INT("&naset, &naindex, &nasore, &nasum not all defined", + procName, 1); if (!recog) return ERROR_INT("recog is not defined", procName, 1); if (!recoga) @@ -1671,12 +1661,11 @@ PIXA *pixa1; PROCNAME("recogCorrelAverages"); - if (!pnaindex) - return ERROR_INT("&naindex not defined", procName, 1); - *pnaindex = NULL; - if (!pnascore) - return ERROR_INT("&nascore not defined", procName, 1); - *pnascore = NULL; + if (ppixadb) *ppixadb = NULL; + if (pnaindex) *pnaindex = NULL; + if (pnascore) *pnascore = NULL; + if (!pnaindex || !pnascore) + return ERROR_INT("&naindex and &nascore not defined", procName, 1); if (!recog1 || !recog2) return ERROR_INT("recog1 and recog2 not both defined", procName, 1); if (!recog1->train_done || !recog2->train_done) @@ -2275,31 +2264,28 @@ PIXA *pixa; /*! - * recogMakeBmf() + * recogResetBmf() * * Input: recog - * fontdir (for bitmap fonts; typically "fonts") * size (of font; even integer between 4 and 20; default is 6) * Return: 0 if OK, 1 on error * * Notes: - * (1) This can be used to (re)set the size of the font used for - * debug labelling. + * (1) Use this to reset the size of the font used for debug labelling. */ l_int32 -recogMakeBmf(L_RECOG *recog, - const char *fontdir, +recogResetBmf(L_RECOG *recog, l_int32 size) { - PROCNAME("recogMakeBmf"); + PROCNAME("recogResetBmf"); - if (!recog || !fontdir) - return ERROR_INT("recog and fontdir not both defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); if (size < 4 || size > 20 || (size % 2)) size = 6; if (size == recog->bmf_size) return 0; /* no change */ bmfDestroy(&recog->bmf); - recog->bmf = bmfCreate(fontdir, size); + recog->bmf = bmfCreate(NULL, size); recog->bmf_size = size; return 0; } @@ -2363,19 +2349,23 @@ PIXA *pixa2; /* * debugAddImage2() * - * Input: &pixadb ( + * Input: &pixadb (ptr required to allow pixadb to be returned; + * input pixadb can be null) * pixa1 ( accumulated pairs of images) * bmf * index (of recog in recoga) * Return: void * * Notes: - * (1) If pixa1 is NULL, do nothing. - * (2) If this is the first time this function is called, then - * *ppixadb == NULL, so we create pixadb (storing the ptr at ppixadb). - * (3) Display pixa1 into a pix and add to pixadb. - * (4) Subsequent calls, for different recognizers that could be used - * for augmenting the instances, add to pixadb. + * (1) This displays pixa1 into a pix and adds it to pixadb. + * (2) If pixa1 is NULL, do nothing. + * (3) The first time this function is called, first initialize: + * Pixa *pixadb = NULL; + * Then: + * debugAddImage2(&pixadb, pixa1, ...); + * This will create pixadb from data in pixa1, storing the ptr + * at ppixadb. Subsequent calls (e.g., for different recognizers + * that could be used to augment the instances) will add to pixadb. */ static void debugAddImage2(PIXA **ppixadb, diff --git a/liblept/src/regutils.c b/liblept/src/regutils.c index bc7d50b..2a55a19 100644 --- a/liblept/src/regutils.c +++ b/liblept/src/regutils.c @@ -140,8 +140,7 @@ L_REGPARAMS *rp; /* 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 = genTempFilename("/tmp/regout", - "regtest_output.txt", 0, 1); + rp->tempfile = genPathname("/tmp/regout", "regtest_output.txt"); rp->fp = fopenWriteStream(rp->tempfile, "wb"); if (rp->fp == NULL) { rp->success = FALSE; @@ -504,17 +503,23 @@ PIX *pix1, *pix2; /* Generate the golden file name; used in 'generate' and 'compare' */ splitPathAtExtension(localname, NULL, &ext); - snprintf(namebuf, sizeof(namebuf), "/tmp/golden/%s_golden.%d%s", + snprintf(namebuf, sizeof(namebuf), "/tmp/golden/%s_golden.%02d%s", rp->testname, rp->index, ext); FREE(ext); /* Generate mode. No testing. */ if (rp->mode == L_REG_GENERATE) { /* Save the file as a golden file */ -/* fprintf(stderr, "%d: %s\n", rp->index, namebuf); */ ret = fileCopy(localname, namebuf); - if (!ret) - fprintf(stderr, "Copy: %s to %s\n", 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); + FREE(local); + FREE(golden); + } +#endif return ret; } @@ -596,7 +601,7 @@ SARRAY *sa; if (rp->mode != L_REG_COMPARE) return 0; /* Generate the golden file names */ - snprintf(namebuf, sizeof(namebuf), "%s_golden.%d.", rp->testname, index1); + snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d.", rp->testname, index1); sa = getSortedPathnamesInDirectory("/tmp/golden", namebuf, 0, 0); if (sarrayGetCount(sa) != 1) { sarrayDestroy(&sa); @@ -607,7 +612,7 @@ SARRAY *sa; name1 = sarrayGetString(sa, 0, L_COPY); sarrayDestroy(&sa); - snprintf(namebuf, sizeof(namebuf), "%s_golden.%d.", rp->testname, index2); + snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d.", rp->testname, index2); sa = getSortedPathnamesInDirectory("/tmp/golden", namebuf, 0, 0); if (sarrayGetCount(sa) != 1) { sarrayDestroy(&sa); @@ -677,7 +682,7 @@ char namebuf[256]; } /* Generate the local file name */ - snprintf(namebuf, sizeof(namebuf), "/tmp/regout/%s.%d.%s", rp->testname, + snprintf(namebuf, sizeof(namebuf), "/tmp/regout/%s.%02d.%s", rp->testname, rp->index + 1, ImageFileFormatExtensions[format]); /* Write the local file */ diff --git a/liblept/src/sarray.c b/liblept/src/sarray.c index d35789c..32ff12f 100644 --- a/liblept/src/sarray.c +++ b/liblept/src/sarray.c @@ -141,8 +141,7 @@ #include "allheaders.h" static const l_int32 INITIAL_PTR_ARRAYSIZE = 50; /* n'importe quoi */ -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; /* Static function */ static l_int32 sarrayExtendArray(SARRAY *sa); diff --git a/liblept/src/sel1.c b/liblept/src/sel1.c index 82683d2..a9fab9d 100644 --- a/liblept/src/sel1.c +++ b/liblept/src/sel1.c @@ -139,10 +139,9 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 256 -#define INITIAL_PTR_ARRAYSIZE 50 /* n'import quoi */ -#define MANY_SELS 1000 +static const l_int32 L_BUF_SIZE = 256; +static const l_int32 INITIAL_PTR_ARRAYSIZE = 50; /* n'import quoi */ +static const l_int32 MANY_SELS = 1000; /* Static functions */ static l_int32 selaExtendArray(SELA *sela); diff --git a/liblept/src/sel2.c b/liblept/src/sel2.c index dab585b..094855f 100644 --- a/liblept/src/sel2.c +++ b/liblept/src/sel2.c @@ -49,8 +49,7 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; /* Linear brick sel sizes, including all those that are required * for decomposable sels up to size 63. */ diff --git a/liblept/src/skew.c b/liblept/src/skew.c index c24a450..86e4c23 100644 --- a/liblept/src/skew.c +++ b/liblept/src/skew.c @@ -245,6 +245,8 @@ 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) @@ -276,10 +278,8 @@ PIX *pixb, *pixd; sweeprange, sweepdelta, DEFAULT_MINBS_DELTA); pixDestroy(&pixb); - if (pangle) - *pangle = angle; - if (pconf) - *pconf = conf; + if (pangle) *pangle = angle; + if (pconf) *pconf = conf; if (ret) return pixClone(pixs); @@ -319,14 +319,14 @@ pixFindSkew(PIX *pixs, { 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); - if (!pangle) - return ERROR_INT("&angle not defined", procName, 1); - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); return pixFindSkewSweepAndSearch(pixs, pangle, pconf, DEFAULT_SWEEP_REDUCTION, @@ -369,16 +369,16 @@ 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 (!pangle) - return ERROR_INT("&angle not defined", procName, 1); if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) return ERROR_INT("reduction must be in {1,2,4,8}", procName, 1); - *pangle = 0.0; /* init */ deg2rad = 3.1415926535 / 180.; ret = 0; @@ -622,14 +622,13 @@ PIX *pixsw, *pixsch, *pixt1, *pixt2; PROCNAME("pixFindSkewSweepAndSearchScorePivot"); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not 1 bpp", procName, 1); - if (!pangle) - return ERROR_INT("&angle not defined", procName, 1); - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); + 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) @@ -639,8 +638,6 @@ PIX *pixsw, *pixsch, *pixt1, *pixt2; if (pivot != L_SHEAR_ABOUT_CORNER && pivot != L_SHEAR_ABOUT_CENTER) return ERROR_INT("invalid pivot", procName, 1); - *pangle = 0.0; - *pconf = 0.0; deg2rad = 3.1415926535 / 180.; ret = 0; @@ -985,9 +982,10 @@ PIX *pixr; PROCNAME("pixFindSkewOrthogonalRange"); + if (pangle) *pangle = 0.0; + if (pconf) *pconf = 0.0; if (!pangle || !pconf) - return ERROR_INT("&angle and &conf not both defined", procName, 1); - *pangle = *pconf = 0.0; + 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); @@ -1050,6 +1048,9 @@ 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); @@ -1120,15 +1121,15 @@ 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); - if (!phratio && !pvratio) - return ERROR_INT("nothing to do", procName, 1); - if (phratio) *phratio = 0.0; - if (pvratio) *pvratio = 0.0; - empty = 0; if (phratio) { na = pixCountPixelsByRow(pixs, NULL); diff --git a/liblept/src/stringcode.c b/liblept/src/stringcode.c new file mode 100644 index 0000000..9c6c335 --- /dev/null +++ b/liblept/src/stringcode.c @@ -0,0 +1,744 @@ +/*====================================================================* + - 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. + *====================================================================*/ + +/* + * 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() + * + * 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" */ +}; + + /* Serializable data types */ +static const l_int32 l_ntypes = 20; +static const struct L_GenAssoc l_assoc[] = { + {0, "INVALID", "invalid", "invalid" }, + {1, "BOXA", "Boxa", "boxaRead" }, + {2, "BOXAA", "Boxaa", "boxaaRead" }, + {3, "L_DEWARP", "Dewarp", "dewarpRead" }, + {4, "L_DEWARPA", "Dewarpa", "dewarpaRead" }, + {5, "L_DNA", "L_Dna", "l_dnaRead" }, + {6, "L_DNAA", "L_Dnaa", "l_dnaaRead" }, + {7, "DPIX", "DPix", "dpixRead" }, + {8, "FPIX", "FPix", "fpixRead" }, + {9, "NUMA", "Numa", "numaRead" }, + {10, "NUMAA", "Numaa", "numaaRead" }, + {11, "PIX", "Pix", "pixRead" }, + {12, "PIXA", "Pixa", "pixaRead" }, + {13, "PIXAA", "Pixaa", "pixaaRead" }, + {14, "PIXACOMP", "Pixacomp", "pixacompRead" }, + {15, "PIXCMAP", "Pixcmap", "pixcmapRead" }, + {16, "PTA", "Pta", "ptaRead" }, + {17, "PTAA", "Ptaa", "ptaaRead" }, + {18, "RECOG", "Recog", "recogRead" }, + {19, "RECOGA", "Recoga", "recogaRead" }, + {20, "SARRAY", "Sarray", "sarrayRead" } +}; + +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 */ +/*---------------------------------------------------------------------*/ +/*! + * strcodeCreate() + * + * Input: 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..c + * autogen..h + */ +L_STRCODE * +strcodeCreate(l_int32 fileno) +{ +L_STRCODE *strcode; + + PROCNAME("strcodeCreate"); + + if ((strcode = (L_STRCODE *)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; +} + + +/*! + * strcodeDestroy() + * + * Input: &strcode (strcode is 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); + FREE(strcode); + *pstrcode = NULL; + return; +} + + +/*! + * strcodeCreateFromFile() + * + * Input: filein (containing filenames of serialized data) + * fileno (integer that labels the two output files) + * outdir ( if null, files are made in /tmp) + * 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..c + * autogen..h + */ +l_int32 +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); + 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); + return 0; +} + + +/*! + * strcodeGenerate() + * + * Input: strcode (for accumulating data) + * filein (input file with serialized data) + * type (of data; use the typedef string) + * Return: 0 if OK, 1 on error. + * + * Notes: + * (1) The generated function name is + * l_autodecode_() + * where 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_int32 +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; +} + + +/*! + * strcodeFinalize() + * + * Input: &strcode (destroys after .c and .h files have been generated) + * outdir ( if null, files are made in /tmp) + * 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"); + + if (!pstrcode || *pstrcode == NULL) + return ERROR_INT("No input data", procName, 1); + strcode = *pstrcode; + if (!outdir) { + L_INFO("no outdir specified; writing to /tmp\n", procName); + realoutdir = stringNew("/tmp"); + } else { + realoutdir = stringNew(outdir); + } + + /* ------------------------------------------------------- */ + /* Make the output autogen.*.c file */ + /* ------------------------------------------------------- */ + + /* Make array of textlines from TEMPLATE1 */ + if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + if ((sa1 = sarrayCreateLinesFromString(filestr, 1)) == NULL) + return ERROR_INT("sa1 not made", procName, 1); + FREE(filestr); + + if ((sa3 = sarrayCreate(0)) == NULL) + return ERROR_INT("sa3 not made", procName, 1); + + /* 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), " * 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 */ + if ((filestr = sarrayToString(sa3, 1)) == NULL) + return ERROR_INT("filestr from sa3 not made", procName, 1); + nbytes = strlen(filestr); + snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno); + l_binaryWrite(buf, "w", filestr, nbytes); + FREE(filestr); + sarrayDestroy(&sa1); + sarrayDestroy(&sa3); + + /* ------------------------------------------------------- */ + /* Make the output autogen.*.h file */ + /* ------------------------------------------------------- */ + + /* Make array of textlines from TEMPLATE2 */ + if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + if ((sa2 = sarrayCreateLinesFromString(filestr, 1)) == NULL) + return ERROR_INT("sa2 not made", procName, 1); + FREE(filestr); + + if ((sa3 = sarrayCreate(0)) == NULL) + return ERROR_INT("sa3 not made", procName, 1); + + /* 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 */ + if ((filestr = sarrayToString(sa3, 1)) == NULL) + return ERROR_INT("filestr from sa3 not made", procName, 1); + nbytes = strlen(filestr); + snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno); + l_binaryWrite(buf, "w", filestr, nbytes); + FREE(filestr); + FREE(realoutdir); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + + /* Cleanup */ + strcodeDestroy(pstrcode); + return 0; +} + + +/*! + * l_getIndexFromType() + * + * Input: type (e.g., "PIXA") + * &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; +} + + +/*! + * l_getIndexFromStructname() + * + * Input: structname (e.g., "Pixa") + * &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; +} + + +/*! + * l_getIndexFromFile() + * + * Input: filename + * &sn ( e.g., "Pixa") + * 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("&sn 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; +} + + +/*! + * l_genDataString() + * + * Input: filein (input file of serialized data) + * 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); + FREE(data1); + FREE(data2); + 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, (char *)";\n", L_COPY); + cdata3 = sarrayToString(sa, 0); + sarrayDestroy(&sa); + return cdata3; +} + + +/*! + * l_genCaseString() + * + * Input: ifunc (index into set of functions in generated file) + * 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"); + stringJoinIP(&code, + " l_binaryWrite(\"/tmp/data.bin\", \"w\", data2, size2);\n"); + snprintf(buf, sizeof(buf), + " result = (void *)%s(\"/tmp/data.bin\");\n", + l_assoc[itype].reader); + stringJoinIP(&code, buf); + stringJoinIP(&code, " FREE(data1);\n"); + stringJoinIP(&code, " FREE(data2);\n"); + stringJoinIP(&code, " break;\n"); + return code; +} + + +/*! + * l_genDescrString() + * + * Input: filein (input file of serialized data) + * ifunc (index into set of functions in generated file) + * 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); + + FREE(tail); + return stringNew(buf); +} + diff --git a/liblept/src/stringtemplate1.txt b/liblept/src/stringtemplate1.txt new file mode 100644 index 0000000..12d0bf0 --- /dev/null +++ b/liblept/src/stringtemplate1.txt @@ -0,0 +1,94 @@ +/*====================================================================* + - 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. + *====================================================================*/ + +/* +--- * autogen.*.c + * + * Automatically generated code for deserializing data from + * compiled strings. + * + * Index Type Deserializer Filename + * ----- ---- ------------ -------- +--- * 0 PIXA pixaRead chars-6.pa +--- * 1 PIXA pixaRead chars-10.pa + */ + +#include +#include "allheaders.h" +--- #include "autogen.*.h" + +/*---------------------------------------------------------------------*/ +/* Auto-generated deserializers */ +/*---------------------------------------------------------------------*/ +/*! +--- * l_autodecode_*() + * + * Input: index into array of functions + * Return: data struct (e.g., pixa) in memory + */ +void * +--- l_autodecode_*(l_int32 index) +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +void *result = NULL; +--- l_int32 nfunc = 2; +--- +--- PROCNAME("l_autodecode_*"); + + if (index < 0 || index >= nfunc) { + L_ERROR("invalid index = %d; must be less than %d\n", procName, + index, nfunc); + return NULL; + } + + /* Unencode selected string, write to file, and read it */ + switch (index) { +--- case 0: +--- data1 = decodeBase64(l_strdata_0, strlen(l_strdata_0), &size1); +--- data2 = zlibUncompress(data1, size1, &size2); +--- l_binaryWrite("/tmp/data.bin", "w", data2, size2); +--- result = (void *)pixaRead("/tmp/data.bin"); +--- FREE(data1); +--- FREE(data2); +--- break; +--- case 1: +--- data1 = decodeBase64(l_strdata_1, strlen(l_strdata_1), &size1); +--- data2 = zlibUncompress(data1, size1, &size2); +--- l_binaryWrite("/tmp/data.bin", "w", data2, size2); +--- result = (void *)pixaRead("/tmp/data.bin"); +--- FREE(data1); +--- FREE(data2); +--- break; + default: + L_ERROR("invalid index", procName); + } + + return result; +} + + diff --git a/liblept/src/stringtemplate2.txt b/liblept/src/stringtemplate2.txt new file mode 100644 index 0000000..20c853a --- /dev/null +++ b/liblept/src/stringtemplate2.txt @@ -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. + *====================================================================*/ + +/* +--- * autogen.*.h + * + * Automatically generated function prototype and associated + * encoded serialized strings. + */ + +--- #ifndef LEPTONICA_AUTOGEN_*_H +--- #define LEPTONICA_AUTOGEN_*_H + +/*---------------------------------------------------------------------*/ +/* Function prototype */ +/*---------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +--- void *l_autodecode_*(l_int32 index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/*---------------------------------------------------------------------*/ +/* Serialized strings */ +/*---------------------------------------------------------------------*/ +--- static const char *l_strdata_0 = +--- "..."; +--- static const char *l_strdata_1 = +--- "..."; +--- [etc] +--- +---#endif /* LEPTONICA_AUTOGEN_*_H */ + diff --git a/liblept/src/tiffio.c b/liblept/src/tiffio.c index 5aca120..6b0d4dd 100644 --- a/liblept/src/tiffio.c +++ b/liblept/src/tiffio.c @@ -71,8 +71,8 @@ * l_int32 pixWriteMemTiff(); * l_int32 pixWriteMemTiffCustom(); * - * Note: You should be using version 3.7.4 of libtiff to be certain - * that all the necessary functions are included. + * Note: To include all necessary functions, use libtiff version 3.7.4 + * (or later) */ #include @@ -81,7 +81,6 @@ #include #else /* _MSC_VER */ #include -#define seek _seek; #endif /* _MSC_VER */ #include #include "allheaders.h" @@ -139,7 +138,8 @@ struct tiff_transform { /* 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. */ + * 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}, @@ -151,6 +151,177 @@ static struct tiff_transform tiff_orientation_transforms[] = { {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; +} /*--------------------------------------------------------------* @@ -214,12 +385,13 @@ TIFF *tif; if (!fp) return (PIX *)ERROR_PTR("stream not defined", procName, NULL); - if ((tif = fopenTiff(fp, "rb")) == NULL) + if ((tif = fopenTiff(fp, "r")) == NULL) return (PIX *)ERROR_PTR("tif not opened", procName, NULL); pagefound = FALSE; pix = NULL; for (i = 0; i < MAX_PAGES_IN_TIFF_FILE; i++) { + TIFFSetDirectory(tif, i); if (i == n) { pagefound = TRUE; if ((pix = pixReadFromTiffStream(tif)) == NULL) { @@ -284,6 +456,7 @@ l_int32 d, wpl, bpl, comptype, i, j, ncolors, rval, gval, bval; l_int32 xres, yres; l_uint32 w, h, tiffword; l_uint32 *line, *ppixel, *tiffdata; +l_uint32 read_oriented; PIX *pix; PIXCMAP *cmap; @@ -292,12 +465,15 @@ PIXCMAP *cmap; if (!tif) return (PIX *)ERROR_PTR("tif not defined", procName, NULL); + read_oriented = 0; + /* Use default fields for bps and spp */ TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); bpp = bps * spp; if (bpp > 32) - return (PIX *)ERROR_PTR("can't handle bpp > 32", procName, NULL); + L_WARNING("bpp = %d; stripping 16 bit rgb samples down to 8\n", + procName, bpp); if (spp == 1) d = bps; else if (spp == 3 || spp == 4) @@ -340,11 +516,14 @@ PIXCMAP *cmap; 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)) { FREE(tiffdata); pixDestroy(&pix); return (PIX *)ERROR_PTR("failed to read tiffdata", procName, NULL); + } else { + read_oriented = 1; } line = pixGetData(pix); @@ -410,8 +589,9 @@ PIXCMAP *cmap; if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) { if (orientation >= 1 && orientation <= 8) { - struct tiff_transform *transform = - &tiff_orientation_transforms[orientation - 1]; + 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) { @@ -577,7 +757,7 @@ TIFF *tif; comptype = IFF_TIFF_ZIP; } - if ((tif = fopenTiff(fp, "wb")) == NULL) + if ((tif = fopenTiff(fp, "w")) == NULL) return ERROR_INT("tif not opened", procName, 1); if (pixWriteToTiffStream(tif, pix, comptype, NULL, NULL, NULL, NULL)) { @@ -1109,7 +1289,7 @@ TIFF *tif; return ERROR_INT("&n not defined", procName, 1); *pn = 0; - if ((tif = fopenTiff(fp, "rb")) == NULL) + if ((tif = fopenTiff(fp, "r")) == NULL) return ERROR_INT("tif not open for read", procName, 1); for (i = 1; i < MAX_PAGES_IN_TIFF_FILE; i++) { @@ -1151,7 +1331,7 @@ TIFF *tif; if (!fp) return ERROR_INT("stream not opened", procName, 1); - if ((tif = fopenTiff(fp, "rb")) == NULL) + if ((tif = fopenTiff(fp, "r")) == NULL) return ERROR_INT("tif not open for read", procName, 1); getTiffStreamResolution(tif, pxres, pyres); TIFFCleanup(tif); @@ -1314,7 +1494,7 @@ TIFF *tif; format != IFF_TIFF_LZW && format != IFF_TIFF_ZIP) return ERROR_INT("file not tiff format", procName, 1); - if ((tif = fopenTiff(fp, "rb")) == NULL) + if ((tif = fopenTiff(fp, "r")) == NULL) return ERROR_INT("tif not open for read", procName, 1); for (i = 0; i < n; i++) { @@ -1488,7 +1668,7 @@ TIFF *tif; if (!fp) return ERROR_INT("stream not defined", procName, 1); - if ((tif = fopenTiff(fp, "rb")) == NULL) + if ((tif = fopenTiff(fp, "r")) == NULL) return ERROR_INT("tif not opened", procName, 1); TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp); *pcomptype = getTiffCompressedFormat(tiffcomp); @@ -1662,19 +1842,15 @@ TIFF *tif; * 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) Requires lseek to rewind to BOF; fseek won't hack it. - * (3) When linking with windows, suggest you use tif_unix.c - * instead of tif_win32.c, because it has been reported that - * the file descriptor returned from fileno() does not work - * with TIFFFdOpen() in tif_win32.c. (win32 requires a - * "handle", which is an integer returned by _get_osfhandle(fd).) + * (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) { -l_int32 fd; - PROCNAME("fopenTiff"); if (!fp) @@ -1682,11 +1858,10 @@ l_int32 fd; if (!modestring) return (TIFF *)ERROR_PTR("modestring not defined", procName, NULL); - if ((fd = fileno(fp)) < 0) - return (TIFF *)ERROR_PTR("invalid file descriptor", procName, NULL); - lseek(fd, 0, SEEK_SET); - - return TIFFFdOpen(fd, "TIFFstream", modestring); + 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); } diff --git a/liblept/src/utils.c b/liblept/src/utils.c index f53694c..5283c23 100644 --- a/liblept/src/utils.c +++ b/liblept/src/utils.c @@ -43,6 +43,7 @@ * l_int32 stringCat() * char *stringConcatNew() * char *stringJoin() + * l_int32 stringJoinIP() * char *stringReverse() * char *strtokSafe() * l_int32 stringSplitOnToken() @@ -142,6 +143,8 @@ * L_TIMER startTimerNested() * l_float32 stopTimerNested() * void l_getCurrentTime() + * L_WALLTIMER *startWallTimer() + * l_float32 stopWallTimer() * void l_getFormattedDate() * * Notes on cross-platform development @@ -579,6 +582,51 @@ l_int32 srclen1, srclen2, destlen; } +/*! + * stringJoinIP() + * + * Input: &src1 string (address of src1; cannot be on the stack) + * src2 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: + * #define stringJoinIP(src1, src2) \ + * {tmpstr = stringJoin((src1),(src2)); \ + * FREE(src1); \ + * (src1) = tmpstr;} + * (5) Another function to consider for joining many strings is + * stringConcatNew(). + */ +l_int32 +stringJoinIP(char **psrc1, + const char *src2) +{ +char *tmpstr; + + PROCNAME("stringJoinIP"); + + if (!psrc1) + return ERROR_INT("&src1 not defined", procName, 1); + + tmpstr = stringJoin(*psrc1, src2); + FREE(*psrc1); + *psrc1 = tmpstr; + return 0; +} + + /*! * stringReverse() * @@ -1706,9 +1754,10 @@ convertOnBigEnd32(l_uint32 wordin) * Notes: * (1) This should be used whenever you want to run fopen() to * read from a stream. Never call fopen() directory. - * (2) This also handles pathname conversions for Windows; in - * particular the rewrite: - * /tmp --> + * (2) This also handles pathname conversions, if necessary: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) */ FILE * fopenReadStream(const char *filename) @@ -1748,9 +1797,10 @@ FILE *fp; * 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 also handles pathname conversions for Windows; in - * particular the rewrite: - * /tmp --> + * (2) This also handles pathname conversions, if necessary: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) */ FILE * fopenWriteStream(const char *filename, @@ -1878,56 +1928,6 @@ lept_free(void *ptr) * Cross-platform file system operations * * [ These only write to /tmp or its subdirectories ] * *--------------------------------------------------------------------*/ -#if 0 /* TODO: REMOVE_THIS */ -/*! - * lept_mkdir() - * - * Input: subdir (of /tmp or its equivalent on Windows) - * Return: 0 on success, non-zero on failure - * - * Notes: - * (1) This makes a subdirectory of the root temp directory. - * The root temp directory is: - * /tmp (unix) - * (windows) - */ -l_int32 -lept_mkdir(const char *subdir) -{ -char *rootdir, *dir; -l_int32 ret; - - PROCNAME("lept_mkdir"); - - 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); - - rootdir = genPathname("/tmp", NULL); - if ((dir = appendSubdirectory(rootdir, subdir)) == NULL) { - FREE(rootdir); - return ERROR_INT("directory name not made", procName, 1); - } - - /* Make the subdirectory */ -#ifndef _WIN32 - ret = mkdir(dir, 0777); -#else - l_uint32 attributes = GetFileAttributes(dir); - ret = 0; - if (attributes == INVALID_FILE_ATTRIBUTES) { - ret = (CreateDirectory(dir, NULL) ? 0 : 1); - } -#endif /* !_WIN32 */ - - FREE(rootdir); - FREE(dir); - return ret; -} -#endif - - /*! * lept_mkdir() * @@ -1936,17 +1936,18 @@ l_int32 ret; * * Notes: * (1) This makes a subdirectory of the root temp directory. - * The root temp directory is: - * /tmp (unix) - * (windows) + * (2) The root temp directory is: + * /tmp (unix) [default] + * /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * /leptonica (windows) */ l_int32 lept_mkdir(const char *subdir) { char *dir; l_int32 ret; -#ifdef _WIN32 char *newpath; +#ifdef _WIN32 l_uint32 attributes; #endif /* _WIN32 */ @@ -1961,9 +1962,12 @@ l_uint32 attributes; /* Make the subdirectory */ #ifndef _WIN32 - ret = mkdir(dir, 0777); + if (ADD_LEPTONICA_SUBDIR == 1) + mkdir("/tmp/leptonica", 0777); + newpath = genPathname(dir, NULL); + ret = mkdir(newpath, 0777); #else - /* Make sure the tmp director exists */ + /* Make sure the tmp directory exists */ newpath = genPathname("/tmp", NULL); attributes = GetFileAttributes(newpath); if (attributes == INVALID_FILE_ATTRIBUTES) { @@ -1973,9 +1977,9 @@ l_uint32 attributes; newpath = genPathname(dir, NULL); ret = (CreateDirectory(newpath, NULL) ? 0 : 1); - FREE(newpath); #endif /* !_WIN32 */ + FREE(newpath); FREE(dir); return ret; } @@ -1988,10 +1992,12 @@ l_uint32 attributes; * Return: 0 on success, non-zero on failure * * Notes: - * (1) This removes all files from the specified subdirectory of: - * /tmp (unix) - * (windows) - * and then removes the directory. + * (1) This removes all files from the specified subdirectory of + * the root temp directory: + * /tmp (unix) [default] + * /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * /leptonica (windows) + * and then removes the subdirectory. * (2) The combination * lept_rmdir(subdir); * lept_mkdir(subdir); @@ -2069,8 +2075,11 @@ char *newpath; * * Notes: * (1) Always use unix pathname separators. - * (2) For windows, does automatic translation to subdirectory - * if the pathname begins with '/tmp'. + * (2) By calling genPathname(), this does an automatic translation + * of "/tmp" if the pathname begins with "/tmp": + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) */ void lept_direxists(const char *dir, @@ -2120,9 +2129,11 @@ char *realdir; * If both @subdir == NULL and @substr == NULL, this removes * all files in /tmp. * (3) Use unix pathname separators. - * (4) On Windows, the file is in either , or in a - * subdirectory, where is the Windows temp dir. - * The name translation is: /tmp --> . + * (4) By calling genPathname(), this does an automatic translation + * of "/tmp" if the pathname begins with "/tmp": + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) * (5) Error conditions: * * returns -1 if the directory is not found * * returns the number of files (> 0) that it was unable to remove. @@ -2165,17 +2176,15 @@ SARRAY *sa; /*! * lept_rm() * - * Input: subdir ( If NULL, the removed file is in /tmp) + * Input: subdir ( of '/tmp'; can be NULL) * tail (filename without the directory) * Return: 0 on success, non-zero on failure * * Notes: - * (1) This removes the named file in /tmp or a subdirectory of /tmp. - * Use NULL for @subdir if the file is in /tmp. - * (2) Use unix pathname separators. - * (3) On Windows, the file is in either , or in a - * subdirectory, where is the Windows temp dir. - * The name translation is: /tmp --> . + * (1) This uses genPathname() to do a name translation from /tmp: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) */ l_int32 lept_rm(const char *subdir, @@ -2208,8 +2217,9 @@ l_int32 ret; * (1) This removes the named file. * (2) Use unix pathname separators. * (3) 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. + * any file -- it is not restricted to files that are in /tmp or a + * subdirectory of it. No path translation is applied for files + * in /tmp or a subdirectory of /tmp. */ l_int32 lept_rmfile(const char *filepath) @@ -2254,10 +2264,12 @@ l_int32 ret; * (5) For debugging, the computed newpath can be returned. It must * be freed by the caller. * (6) Reminders: - * (a) use unix pathname separators - * (b) on windows, there is a name translation from - * /tmp --> - * (7) Examples: + * (a) use unix pathname separators + * (b) Uses directory translation for files in /tmp: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) + * (7) Examples before directory translation: * * newdir = NULL, newtail = NULL ==> /tmp/src-tail * * newdir = NULL, newtail = abc ==> /tmp/abc * * newdir = def, newtail = NULL ==> /tmp/def/src-tail @@ -2278,13 +2290,16 @@ l_int32 ret; if (!srcfile) return ERROR_INT("srcfile not defined", procName, 1); - /* Get canonical src pathname */ + /* Require output pathname to be in /tmp/ or a subdirectory */ + if (makeTempDirname(newtemp, 256, newdir) == 1) + return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1); + + /* Get canonical src pathname */ splitPathAtDirectory(srcfile, &dir, &srctail); srcpath = genPathname(dir, srctail); FREE(dir); - /* Require output pathname to be in /tmp/ or a subdirectory */ - makeTempDirname(newtemp, 256, newdir); + /* Generate output pathname */ if (!newtail || newtail[0] == '\0') newpath = genPathname(newtemp, srctail); else @@ -2331,9 +2346,11 @@ l_int32 ret; * (5) For debugging, the computed newpath can be returned. It must * be freed by the caller. * (6) Reminders: - * (a) use unix pathname separators - * (b) on windows, there is an additional name translation from - * /tmp --> + * (a) use unix pathname separators + * (b) Uses directory translation for files in /tmp: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) * (7) Examples: * * newdir = NULL, newtail = NULL ==> /tmp/src-tail * * newdir = NULL, newtail = abc ==> /tmp/abc @@ -2356,13 +2373,16 @@ l_int32 ret; if (!srcfile) return ERROR_INT("srcfile not defined", procName, 1); + /* Require output pathname to be in /tmp or a subdirectory */ + if (makeTempDirname(newtemp, 256, newdir) == 1) + return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1); + /* Get canonical src pathname */ splitPathAtDirectory(srcfile, &dir, &srctail); srcpath = genPathname(dir, srctail); FREE(dir); - /* Require output pathname to be in /tmp or a subdirectory */ - makeTempDirname(newtemp, 256, newdir); + /* Generate output pathname */ if (!newtail || newtail[0] == '\0') newpath = genPathname(newtemp, srctail); else @@ -2730,8 +2750,11 @@ size_t len; * @fname, with @dir == NULL. * * if in a "/tmp" directory and on windows, the windows * temp directory is used. - * (2) The name translation for "/tmp" on windows is: - * /tmp --> (windows) + * (2) If the root of @dir is '/tmp', this does name translation: + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) + * where is the windows temp directory. * (3) 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 @@ -2775,25 +2798,43 @@ l_int32 dirlen, namelen, size; if ((pathout = (char *)CALLOC(size, sizeof(char))) == NULL) return (char *)ERROR_PTR("pathout not made", procName, NULL); - /* First handle the @dir (which may be a full pathname) */ -#ifdef _WIN32 + /* First handle @dir (which may be a full pathname) */ if (strncmp(cdir, "/tmp", 4) != 0) { /* not in /tmp; OK as is */ stringCopy(pathout, cdir, dirlen); } else { /* in /tmp */ +#ifdef _WIN32 /* Start with the temp dir */ char dirt[MAX_PATH]; GetTempPath(sizeof(dirt), dirt); /* get the windows temp directory */ stringCopy(pathout, dirt, strlen(dirt) - 1); + /* Add a special subdirectory if it's not already there. This is + * advantageous for WIN32 because it puts all temporary files in + * a single subdirectory of , facilitating cleanup. */ + if (dirlen <= 5 || + (dirlen > 5 && strncmp(cdir + 5, "leptonica", 9) != 0)) + stringCat(pathout, size, "/leptonica"); + /* Add the rest of cdir */ if (dirlen > 4) stringCat(pathout, size, cdir + 4); - } #else /* unix */ - stringCopy(pathout, cdir, dirlen); + /* Add subdirectory if requested and it is not already in cdir. + * For the latter, add if either cdir is just /tmp or /tmp/, or + * if there is a subdirectory of /tmp but it's not "leptonica". */ + if (ADD_LEPTONICA_SUBDIR && + (dirlen <= 5 || + (dirlen > 5 && strncmp(cdir + 5, "leptonica", 9) != 0))) { + stringCopy(pathout, "/tmp/leptonica", 14); + if (dirlen > 4) + stringCat(pathout, size, cdir + 4); + } else { /* OK as is */ + stringCopy(pathout, cdir, dirlen); + } #endif /* _WIN32 */ + } - /* Now handle fname */ + /* Now handle @fname */ if (fname && strlen(fname) > 0) { dirlen = strlen(pathout); pathout[dirlen] = '/'; @@ -2815,11 +2856,13 @@ l_int32 dirlen, namelen, size; * * 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 - * /, where is "/tmp" on unix - * and some other path on windows, determined by the windows - * function GenTempPath(). + * written into @result with unix separators. + * (2) Caller allocates @result, large enough to hold the path, + * which is: + * /tmp/@subdir (unix) [default] + * /tmp/leptonica/@subdir (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * /leptonica/@subdir (windows) + * where is a path on windows determined by GenTempPath(). * (3) Usage example: * char result[256]; * makeTempDirname(result, 256, "golden"); @@ -2829,7 +2872,9 @@ makeTempDirname(char *result, size_t nbytes, const char *subdir) { -size_t len; +char *dir, *path; +l_int32 ret = 0; +size_t pathlen; PROCNAME("makeTempDirname"); @@ -2838,23 +2883,20 @@ size_t len; if (subdir && ((subdir[0] == '.') || (subdir[0] == '/'))) return ERROR_INT("subdir not an actual subdirectory", procName, 1); - /* Start with directory */ -#ifdef _WIN32 - char dirt[MAX_PATH]; - GetTempPath(sizeof(dirt), dirt); - snprintf(result, nbytes, "%s", dirt); -#else - snprintf(result, nbytes, "%s", "/tmp"); -#endif /* _WIN32 */ - - /* Optionally add input subdirectory */ - if (subdir) { - len = strlen(result); - strncat(result, "/", nbytes - len); - strncat(result, subdir, nbytes - len - 1); + memset(result, 0, nbytes); + dir = pathJoin("/tmp", subdir); + path = genPathname(dir, NULL); + pathlen = strlen(path); + if (pathlen < nbytes - 1) { + strncpy(result, path, pathlen); + } else { + L_ERROR("result array too small for path\n", procName); + ret = 1; } - return 0; + FREE(dir); + FREE(path); + return ret; } @@ -2928,10 +2970,12 @@ size_t len; * (6) N.B. The caller is responsible for freeing the returned filename. * For windows, to avoid C-runtime boundary crossing problems * when using DLLs, you must use lept_free() to free the name. - * (7) For windows, if the caller requests the directory '/tmp', - * this uses GetTempPath() to select the actual directory, - * avoiding platform-conditional code in use. We represent - * the Windows temp directory by . + * (7) When @dir is /tmp or a subdirectory of /tmp, genPathname() + * does a name translation for '/tmp': + * ==> /tmp (unix) [default] + * ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1] + * ==> /leptonica (windows) + * where is a path on windows determined by GenTempPath(). * (8) Set @usetime = @usepid = 1 when * (a) more than one process is writing and reading temp files, or * (b) multiple threads from a single process call this function, or @@ -3311,11 +3355,11 @@ static struct rusage rusage_after; /*! * startTimer(), stopTimer() * - * Example of usage: - * - * startTimer(); - * .... - * fprintf(stderr, "Elapsed time = %7.3f sec\n", 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) @@ -3498,6 +3542,55 @@ LONGLONG usecs; #endif +/*! + * startWallTimer() + * Input: void + * Return: walltimer-ptr + * + * stopWallTimer() + * Input: &walltimer-ptr + * Return: time (wall time elapsed in seconds) + * + * 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 *)CALLOC(1, sizeof(L_WALLTIMER)); + l_getCurrentTime(&timer->start_sec, &timer->start_usec); + return timer; +} + +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; + FREE(timer); + *ptimer = NULL; + return (tsec + ((l_float32)tusec) / 1000000.0); +} + + /*! * l_getFormattedDate() * diff --git a/liblept/src/viewfiles.c b/liblept/src/viewfiles.c index b0f076f..4860eb4 100644 --- a/liblept/src/viewfiles.c +++ b/liblept/src/viewfiles.c @@ -38,8 +38,7 @@ #include /* for CreateDirectory() */ #endif -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; static const l_int32 DEFAULT_THUMB_WIDTH = 120; static const l_int32 DEFAULT_VIEW_WIDTH = 800; static const l_int32 MIN_THUMB_WIDTH = 50; diff --git a/liblept/src/writefile.c b/liblept/src/writefile.c index dfd158b..e72645a 100644 --- a/liblept/src/writefile.c +++ b/liblept/src/writefile.c @@ -30,6 +30,7 @@ * High-level procedures for writing images to file: * l_int32 pixaWriteFiles() * l_int32 pixWrite() [behavior depends on WRITE_AS_NAMED] + * l_int32 pixWriteAutoFormat() * l_int32 pixWriteStream() * l_int32 pixWriteImpliedFormat() * l_int32 pixWriteTempfile() @@ -37,6 +38,7 @@ * Selection of output format if default is requested * l_int32 pixChooseOutputFormat() * l_int32 getImpliedFileFormat() + * l_int32 pixGetAutoFormat() * const char *getFormatExtension() * * Write to memory @@ -64,6 +66,7 @@ * 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) @@ -72,7 +75,6 @@ * 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) - * (4) No other output formats are supported, such as jp2 (jpeg2000) */ #include @@ -92,8 +94,7 @@ static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN; /* default */ static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV; /* default */ #endif /* _WIN32 */ -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; static const l_int32 MAX_DISPLAY_WIDTH = 1000; static const l_int32 MAX_DISPLAY_HEIGHT = 800; static const l_int32 MAX_SIZE_FOR_PNG = 200; @@ -292,20 +293,38 @@ FILE *fp; fclose(fp); return ERROR_INT("pix not written to stream", procName, 1); } - - /* Close the stream except if GIF under windows, because - * EGifCloseFile() closes the windows file stream! */ - if (format != IFF_GIF) - fclose(fp); -#ifndef _WIN32 - else /* gif file */ - fclose(fp); -#endif /* ! _WIN32 */ + fclose(fp); return 0; } +/*! + * pixWriteAutoFormat() + * + * Input: filename + * pix + * Return: 0 if OK; 1 on error + */ +l_int32 +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); +} + + /*! * pixWriteStream() * @@ -366,7 +385,7 @@ pixWriteStream(FILE *fp, break; case IFF_JP2: - return pixWriteStreamJp2k(fp, pix, 34, 0, 0); + return pixWriteStreamJp2k(fp, pix, 34, 0, 0, 0); break; case IFF_WEBP: @@ -572,6 +591,51 @@ l_int32 format = IFF_UNKNOWN; } +/*! + * pixGetAutoFormat() + * + * Input: pix + * &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_int32 +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; +} + + /*! * getFormatExtension() * @@ -670,7 +734,7 @@ l_int32 ret; break; case IFF_JP2: - ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0); + ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0); break; case IFF_WEBP: diff --git a/liblept/src/xtractprotos.c b/liblept/src/xtractprotos.c index ca4477d..e5ebe89 100644 --- a/liblept/src/xtractprotos.c +++ b/liblept/src/xtractprotos.c @@ -85,8 +85,7 @@ #include #include "allheaders.h" -/* MS VC++ can't handle array initialization with static consts ! */ -#define L_BUF_SIZE 512 +static const l_int32 L_BUF_SIZE = 512; /* Cygwin needs an extension to prevent it from appending * ".exe" to the filename */ diff --git a/liblept/version-notes.html b/liblept/version-notes.html index 0a8dc88..ed392be 100644 --- a/liblept/version-notes.html +++ b/liblept/version-notes.html @@ -22,6 +22,34 @@

 
+1.72   5 Apr 15
+       Better handling of 1 bpp colormap read/write with png so that
+       they are losseless.  The colormap is always removed on read and
+       the conversion is to the simplest non-cmapped pix that can fully
+       represent the input -- both with and without alpha.
+       Fixed overflow bug in pixCorrelationBinary().
+       Fixed orientation flags and handling of 16 bit RGB in tiff.
+       Also new wrappers to TIFFClientOpen(), so we no longer go through
+       the file descriptor for memory operations.
+       Improvements in the dewarp functions.
+       New box sequence smoothings.
+       New antialiased painting through mask; previously it was only
+       implemented for connected components in a mask.
+       Better error handling and debug output with jpeg2000 read/write.
+       Implemented base64 encoding.  This allows binary data to be represented
+       as a C string that can be compiled.  Used this in bmf utility.
+       Implemented automatic code generation for deserialization from
+       compiled strings (stringcode.*)
+       Regression tests write to leptonica subdir of  in windows; in
+       unix it is optional.  This avoids spamming the  directory.
+       Added new colorspace conversions (XYZ, LAB).
+       New source files: encoding.c, bmfdata.h, stringcode.c, stringcode.h,
+         bootnumgen.c.
+       Removed source files: convolvelow.c, graymorphlow.c
+       New programs: genfonts_reg, colorize_reg, texturefill_reg,
+         autogentest1, autogentest2.
+       alltests_reg now has 66 tests.
+
 1.71   18 Jun 14
        This version supports tesseract 3.0.4.  In particular, 3.0.4
         has automatic conversion of a set of scanned images, either in a
diff --git a/libpng/libpng.vcxproj b/libpng/libpng.vcxproj
index ed3d370..f6d347b 100644
--- a/libpng/libpng.vcxproj
+++ b/libpng/libpng.vcxproj
@@ -1,5 +1,5 @@
 
-
+
   
     
       Debug
@@ -22,23 +22,24 @@
     libpng
     {0008960E-E0DD-41A6-8265-00B31DDB4C21}
     libpng-143
+    8.1
   
   
   
     StaticLibrary
-    v120
+    v140_xp
   
   
     StaticLibrary
-    v120
+    v140_xp
   
   
     StaticLibrary
-    v120
+    v140_xp
   
   
     StaticLibrary
-    v120
+    v140_xp
   
   
   
diff --git a/libtiff/libtiff/libtiff.vcxproj b/libtiff/libtiff/libtiff.vcxproj
index cafc805..76736a6 100644
--- a/libtiff/libtiff/libtiff.vcxproj
+++ b/libtiff/libtiff/libtiff.vcxproj
@@ -1,5 +1,5 @@
 
-
+
   
     
       Debug
@@ -22,27 +22,28 @@
     {0402F7E8-9BE8-4D59-A789-BBB96B284856}
     Win32Proj
     libtiff-394
+    8.1
   
   
   
     StaticLibrary
     true
-    v120
+    v140_xp
   
   
     StaticLibrary
     true
-    v120
+    v140_xp
   
   
     StaticLibrary
     false
-    v120
+    v140_xp
   
   
     StaticLibrary
     false
-    v120
+    v140_xp
   
   
   
diff --git a/vs2013+64bit_support-git.patch b/vs2013+64bit_support-git.patch
index a09d686..7667b79 100644
--- a/vs2013+64bit_support-git.patch
+++ b/vs2013+64bit_support-git.patch
@@ -8,9 +8,9 @@ Index: vs2013/include/leptonica_versionnumbers.props
 +  
 +    416
 +    8c
-+    171
-+    1,71,0,0
-+    1.71
++    172
++    1,72,0,0
++    1.72
 +    143
 +    394
 +    128
@@ -4155,9 +4155,9 @@ Index: vs2013/include/leptonica_versionnumbers.props
 +  
 +    416
 +    8c
-+    171
-+    1,71,0,0
-+    1.71
++    172
++    1,72,0,0
++    1.72
 +    143
 +    394
 +    128
@@ -4234,9 +4234,9 @@ Index: vs2013/include/leptonica_versionnumbers.props
 +  
 +    416
 +    8c
-+    171
-+    1,71,0,0
-+    1.71
++    172
++    1,72,0,0
++    1.72
 +    143
 +    394
 +    128
diff --git a/vs2013+64bit_support.patch b/vs2013+64bit_support.patch
index 908720d..98d6bcc 100644
--- a/vs2013+64bit_support.patch
+++ b/vs2013+64bit_support.patch
@@ -53,9 +53,9 @@ index 0000000..5d3a00b
 +  
 +    416
 +    8c
-+    171
-+    1,71,0,0
-+    1.71
++    172
++    1,72,0,0
++    1.72
 +    143
 +    394
 +    128
diff --git a/zlib/zlibstat.vcxproj b/zlib/zlibstat.vcxproj
index 97e9af0..1a4c0b6 100644
--- a/zlib/zlibstat.vcxproj
+++ b/zlib/zlibstat.vcxproj
@@ -1,5 +1,5 @@
 
-
+
   
     
       Debug
@@ -21,27 +21,28 @@
   
     {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}
     zlib-128
+    8.1
   
   
   
     StaticLibrary
     false
-    v120
+    v140_xp
   
   
     StaticLibrary
     false
-    v120
+    v140_xp
   
   
     StaticLibrary
     false
-    v120
+    v140_xp
   
   
     StaticLibrary
     false
-    v120
+    v140_xp