diff --git a/AUTHORS b/AUTHORS index 75b0cac..b139cd7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ Contributors: Misty De Meo zdenop Steven Lee http://www.rubypdf.com +Radim Hatlapatka diff --git a/ChangeLog b/ChangeLog index 4829a6f..b21f638 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,8 @@ * version info (-V --version) * pdf.py now correctly retains DPI from input images (thanks to Steven Lee http://blog.rubypdf.com/2011/09/09/jbig2-pdf-py-patch-the-right-way-to-get-dpi/) + * R. Hatlapatka: option to use autoThresholding. Improved version from + bachelor thesis JBIG2 compression http://is.muni.cz/th/208155/fi_b/. 0.27: * Update to the latest Leptonica (1.58) diff --git a/src/Makefile.am b/src/Makefile.am index ad87946..121bc88 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,9 @@ AM_CXXFLAGS = -Wall lib_LTLIBRARIES = libjbig2enc.la -libjbig2enc_la_SOURCES = jbig2enc.cc jbig2arith.cc jbig2sym.cc -include_HEADERS = jbig2arith.h jbig2sym.h jbig2structs.h jbig2segments.h +libjbig2enc_la_SOURCES = jbig2enc.cc jbig2arith.cc jbig2sym.cc result.cc jbig2comparator.cc +libjbig2enc_la_LDFLAGS = -no-undefined -version-info $(GENERIC_LIBRARY_VERSION) +include_HEADERS = jbig2arith.h jbig2sym.h jbig2structs.h jbig2segments.h result.h jbig2comparator.h bin_PROGRAMS = jbig2 jbig2_SOURCES = jbig2.cc diff --git a/src/jbig2.cc b/src/jbig2.cc index f04b523..cf6224a 100644 --- a/src/jbig2.cc +++ b/src/jbig2.cc @@ -54,6 +54,8 @@ usage(const char *argv0) { fprintf(stderr, " -4: upsample 4x before thresholding\n"); fprintf(stderr, " -S: remove images from mixed input and save separately\n"); fprintf(stderr, " -j --jpeg-output: write images from mixed input as JPEG\n"); + fprintf(stderr, " -a --autoThresh: engage using autoThresholding for symbol coder\n"); + fprintf(stderr, " --nohash: only for debugging purposes to allow easily compare performance with older version\n"); fprintf(stderr, " -V --version: version info\n"); fprintf(stderr, " -v: be verbose\n"); } @@ -209,13 +211,15 @@ main(int argc, char **argv) { l_int32 img_fmt = IFF_PNG; const char *img_ext = "png"; bool segment = false; + bool autoThresh = false; + bool hash = true; int i; - - #ifdef WIN32 - int result = _setmode(_fileno(stdout), _O_BINARY); - if (result == -1) - fprintf(stderr, "Cannot set mode to binary for stdout\n"); - #endif + + #ifdef WIN32 + int result = _setmode(_fileno(stdout), _O_BINARY); + if (result == -1) + fprintf(stderr, "Cannot set mode to binary for stdout\n"); + #endif for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "-h") == 0 || @@ -327,6 +331,18 @@ main(int argc, char **argv) { continue; } + // engage auto thresholding + if (strcmp(argv[i], "--autoThresh") == 0 || + strcmp(argv[i], "-a") == 0 ) { + autoThresh = true; + continue; + } + + if (strcmp(argv[i], "--nohash") == 0) { + hash = false; + continue; + } + if (strcmp(argv[i], "-v") == 0) { verbose = true; continue; @@ -340,7 +356,7 @@ main(int argc, char **argv) { usage(argv[0]); return 4; } - + if (refine && !symbol_mode) { fprintf(stderr, "Refinement makes not sense unless in symbol mode!\n"); fprintf(stderr, "(if you have -r, you must have -s)\n"); @@ -360,9 +376,9 @@ main(int argc, char **argv) { if (subimage==numsubimages) { subimage = numsubimages = 0; FILE *fp; - if (verbose) fprintf(stderr, "Processing \"%s\"...\n", argv[i]); - if ((fp=fopen(argv[i], "r"))==NULL) { - fprintf(stderr, "Unable to open \"%s\"\n", argv[i]); + if (verbose) fprintf(stderr, "Processing \"%s\"...\n", argv[i]); + if ((fp=lept_fopen(argv[i], "r"))==NULL) { + fprintf(stderr, "Unable to open \"%s\"\n", argv[i]); return 1; } l_int32 filetype; @@ -370,7 +386,7 @@ main(int argc, char **argv) { if (filetype==IFF_TIFF && tiffGetCount(fp, &numsubimages)) { return 1; } - fclose(fp); + lept_fclose(fp); } PIX *source; @@ -455,6 +471,14 @@ main(int argc, char **argv) { } } + if (autoThresh) { + if (hash) { + autoThresholdUsingHash(ctx); + } else { + autoThreshold(ctx); + } + } + uint8_t *ret; int length; ret = jbig2_pages_complete(ctx, &length); diff --git a/src/jbig2comparator.cc b/src/jbig2comparator.cc new file mode 100644 index 0000000..a66ca8d --- /dev/null +++ b/src/jbig2comparator.cc @@ -0,0 +1,386 @@ +// Copyright 2006 Google Inc. All Rights Reserved. +// Author: hata.radim@gmail.com (Radim Hatlapatka) +// +// Copyright (C) 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#include +#endif + +#include + +#include +#if defined(sun) +#include +#else +#include +#endif + +#define u64 uint64_t +#define u32 uint32_t +#define u16 uint16_t +#define u8 uint8_t + + +l_uint32 ** allocateMatrix(int xSize, int ySize) { + l_uint32 **matrix = new l_uint32*[xSize]; + for (int i = 0; i < xSize; i++) { + matrix[i] = new l_uint32[ySize]; + } + return matrix; +} + +void freeMatrix(l_uint32 **matrix, int xSize) { + for (int i = 0; i < xSize; i++) { + delete[] matrix[i]; + } + delete[] matrix; +} + +/* Print n as a binary number + * just for testing + */ +void printbitssimple(unsigned int n) { + unsigned int i; + i = 1<<(sizeof(n) * 8 - 1); + + while (i > 0) { + if (n & i) { + fprintf(stderr,"1"); + } + else { + fprintf(stderr,"0"); + } + i >>= 1; + } +} + + +/* Print n as a binary number + * just for testing + */ +void charprintbitssimple(char ch) { + unsigned int i; + i = 1<<(sizeof(ch) * 8 - 1); + + while (i > 0) { + if (ch & i) { + fprintf(stderr,"1"); + } + else { + fprintf(stderr,"0"); + } + i >>= 1; + } +} + + +char * intsToChars(PIX *pix) { + l_uint32 h = pixGetHeight(pix); + l_uint32 cpl = (pixGetWidth(pix)+7) / 8; + fprintf(stderr, "cpl = %d\n", cpl); + l_uint32 wpl = pixGetWpl(pix); + l_uint32 *dataAsInts = pixGetData(pix); + char * dataAsChars; + dataAsChars = (char*)calloc((h*cpl)+1,sizeof(char)); + int position = 0; + for (l_uint16 i=0; i < h; i++) { + for (l_uint16 j=0; j> 24) & 0xFF); + dataAsChars[position++] = (char)((num >> 16) & 0xFF); + dataAsChars[position++] = (char)((num >> 8) & 0xFF); + dataAsChars[position++] = (char)(num & 0xFF); + } + position -= ((wpl*4) - cpl); + } + dataAsChars[position] = '\0'; + return dataAsChars; +} + + +/** + * printing pix bitmap to stderr -- just for testing + */ +void printPix(PIX *pix) { + if (pix == NULL) { + fprintf(stderr, "Unable to write PIX"); + } + l_uint32 w = pixGetWidth(pix); + l_uint32 h = pixGetHeight(pix); + l_uint32 initUnsigned = 0; + l_uint32 *pval = &initUnsigned; + + fprintf(stderr, "output before conversion (default) as *l_uint32\n"); + for (l_uint16 i = 0; i < h; i++) { + for (l_uint16 j = 0; j < w; j++) { + if (pixGetPixel(pix, j, i, pval)) { + fprintf(stderr, "unable to read pixel from pix\n"); + break; + } + fprintf(stderr, "%d",*pval); + } + fprintf(stderr, "\n"); + } + +} + + +/** + * compare two pix and tell if they are equivalent by trying to decide + * if these symbols look the same for user or not + * it works by finding acumulations of differences between these two templates + * if the difference is bigger than concrete percentage of one of templates than these templates + * if such difference doesn't exist than they are equivalent + */ +int areEquivalent(PIX *const firstTemplate, PIX *const secondTemplate) { + l_int32 w, h, d; + + // checking if they have the same size and depth + if (!pixSizesEqual(firstTemplate, secondTemplate)) { + return 0; + } + + l_int32 firstWpl = pixGetWpl(firstTemplate); + l_int32 secondWpl = pixGetWpl(secondTemplate); + + if (firstWpl != secondWpl) { + return 0; + } + + PIX * pixd; + pixd = pixXor(NULL, firstTemplate, secondTemplate); + + pixGetDimensions(pixd, &w, &h, &d); + + l_int32 init = 0; + l_int32 *pcount = &init; + l_int32 *above = &init; + + + // counting number of ON pixels in firstTemplate + if (pixCountPixels(firstTemplate, pcount, NULL)) { + fprintf(stderr, "Unable to count pixels\n"); + pixDestroy(&pixd); + return 0; + } + + // just for speed up if two symbols are very different + l_int32 thresh = (*pcount) * 0.25; + if (pixThresholdPixelSum(pixd, thresh, above, NULL)) { + fprintf(stderr, "Unable to count pixels of XORed pixes\n"); + pixDestroy(&pixd); + return 0; + } + + + if ((*above) == 1) { + pixDestroy(&pixd); + return 0; + } + + l_uint32 initUnsigned = 0; + l_uint32 *pval = &initUnsigned; + const int divider = 9; + const int vertical = divider * 2; + const int horizontal = divider * 2; + + l_uint32 parsedPixCounts[divider][divider]; + l_uint32 horizontalParsedPixCounts[horizontal][divider]; + l_uint32 verticalParsedPixCounts[divider][vertical]; + + if (d != 1) { + return 0; + } + + int verticalPart = h/divider; + int horizontalPart = w/divider; + + int horizontalModuleCounter = 0; + int verticalModuleCounter = 0; + + + // counting area of elipse and taking percentage of it as pointThresh + int a; + int b; + if (verticalPart < horizontalPart) { + a = horizontalPart / 2; + b = verticalPart / 2; + } else { + a = verticalPart / 2; + b = horizontalPart / 2; + } + + float pointThresh = a * b * M_PI; + l_int32 vlineThresh = (verticalPart * (horizontalPart/2))*0.9; + l_int32 hlineThresh = (horizontalPart * (verticalPart/2))*0.9; + + + /* + * going through submatrixes + */ + for (int horizontalPosition=0; horizontalPosition < divider; horizontalPosition++) { + int horizontalStart = horizontalPart*horizontalPosition + horizontalModuleCounter; + int horizontalEnd; + if (horizontalPosition == (divider-1)) { + horizontalModuleCounter = 0; + horizontalEnd = w; + } else { + if (((w - horizontalModuleCounter) % divider)>0) { + horizontalEnd = horizontalStart + horizontalPart + 1; + horizontalModuleCounter++; + } else { + horizontalEnd = horizontalStart + horizontalPart; + } + } + + // zkus spustit ve vlaknech + for (int verticalPosition=0; verticalPosition < divider; verticalPosition++) { + int verticalStart = verticalPart*verticalPosition + verticalModuleCounter; + int verticalEnd; + if (verticalPosition == (divider-1)) { + verticalModuleCounter = 0; + verticalEnd = h; + } else { + if (((h - verticalModuleCounter) % divider)>0) { + verticalEnd = verticalStart + verticalPart + 1; + verticalModuleCounter++; + } else { + verticalEnd = verticalStart + verticalPart; + } + } + + // making sum of ON pixels in submatrix and saving the result to matrix of sums + int leftCounter = 0; + int rightCounter = 0; + int downCounter = 0; + int upCounter = 0; + + int midleOfHorizontalPart = (horizontalStart + horizontalEnd) / 2; + int midleOfVerticalPart = (verticalStart + verticalEnd) / 2; + + for (int i = horizontalStart; i < horizontalEnd; i++) { + for (int j = verticalStart; j < verticalEnd; j++) { + if (pixGetPixel(pixd, i, j, pval)) { + fprintf(stderr, "unable to read pixel from pix\n"); + break; + } + + if (*pval == 1) { + if (i < midleOfHorizontalPart) { + leftCounter++; + } else { + rightCounter++; + } + if (j < midleOfVerticalPart) { + upCounter++; + } else { + downCounter++; + } + } + } + } + parsedPixCounts[horizontalPosition][verticalPosition] = leftCounter + rightCounter; + + horizontalParsedPixCounts[horizontalPosition*2][verticalPosition] = leftCounter; + horizontalParsedPixCounts[(horizontalPosition*2)+1][verticalPosition] = rightCounter; + + verticalParsedPixCounts[horizontalPosition][verticalPosition*2] = upCounter; + verticalParsedPixCounts[horizontalPosition][(verticalPosition*2)+1] = downCounter; + + } + } + + // destroying XORed pix -- all needed informations are gathered already + pixDestroy(&pixd); + + // checking for horizontal lines + for (int i = 0; (i < (divider*2)-1); i++) { + for (int j = 0; j < (divider-1); j++) { + int horizontalSum = 0; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + horizontalSum += horizontalParsedPixCounts[i+x][j+y]; + } + } + if (horizontalSum > hlineThresh) { + return 0; + } + } + } + + // checking for vertical lines + for (int i = 0; i < (divider-1); i++) { + for (int j = 0; j < ((divider*2)-1); j++) { + int verticalSum = 0; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + verticalSum += verticalParsedPixCounts[i+x][j+y]; + } + } + if (verticalSum > vlineThresh) { + return 0; + } + } + } + + // checking for (cross lines) + for (int i = 0; i < (divider - 2); i++) { + for (int j = 0; j < (divider - 2); j++) { + int leftCross = 0; + int rightCross = 0; + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + if (x == y) { + leftCross += parsedPixCounts[i+x][j+y]; + } + if ((2-x) == y) { + rightCross += parsedPixCounts[i+x][j+y]; + } + } + } + if ((leftCross > hlineThresh) || (rightCross > hlineThresh)) { + return 0; + } + } + } + + /* + * checking if four submatrixes of xored PIX data contains more ON pixels + * than concrete percentage of ON pixels of firstTemplate + */ + for (int i = 0; i < (divider-1); i++) { + for (int j = 0; j < (divider-1); j++) { + int sum = 0; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + sum += parsedPixCounts[i+x][j+y]; + } + } + if (sum > pointThresh) { + return 0; + } + } + } + return 1; +} diff --git a/src/jbig2comparator.h b/src/jbig2comparator.h new file mode 100644 index 0000000..c6e5800 --- /dev/null +++ b/src/jbig2comparator.h @@ -0,0 +1,61 @@ +// Copyright 2006 Google Inc. All Rights Reserved. +// Author: hata.radim@gmail.com (Radim Hatlapatka) +// +// Copyright (C) 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef JBIG2COMPARATOR_H__ +#define JBIG2COMPARATOR_H__ + +// ----------------------------------------------------------------------------- +// Welcome gentle reader, +// +// This is an encoder for JBIG2: +// www.jpeg.org/public/fcd14492.pdf +// +// JBIG2 encodes bi-level (1 bpp) images using a number of clever tricks to get +// better compression than G4. This encoder can: +// * Generate JBIG2 files, or fragments for embedding in PDFs +// * Generic region encoding +// * Symbol extraction, classification and text region coding +// +// It uses the (Apache-ish licensed) Leptonica library: +// http://www.leptonica.com/ +// ----------------------------------------------------------------------------- + +#if defined(sun) +#include +#else +#include +#endif + +#include + +struct Pix; + +/** + * compare two pix and tell if they are equivalent by trying to decide + * if these symbols look the same for user or not + * it works by finding acumulations of differences between these two templates + * if the difference is bigger than concrete percentage of one of templates than these templates + * if such difference doesn't exist than they are equivalent + */ +int areEquivalent(PIX *const firstTemplate, PIX *const secondTemplate); + +/** + * printing pix bitmap to stderr -- just for testing + */ +void printPix(PIX *pix); + +#endif // JBIG2OCR_JBIG2_H__ diff --git a/src/jbig2enc.cc b/src/jbig2enc.cc index 13d9124..c2d330d 100644 --- a/src/jbig2enc.cc +++ b/src/jbig2enc.cc @@ -16,6 +16,7 @@ // limitations under the License. #include +#include #include #include @@ -40,6 +41,10 @@ #include "jbig2sym.h" #include "jbig2structs.h" #include "jbig2segments.h" +#include "jbig2comparator.h" +#include "result.h" + +using namespace std; // ----------------------------------------------------------------------------- // Returns the version identifier as a static string. @@ -136,6 +141,384 @@ jbig2_init(float thresh, float weight, int xres, int yres, bool full_headers, return ctx; } +// causes stack overflow +void reindexing(struct jbig2ctx *ctx, int newIndex, int oldIndex) { + if (!ctx) { + fprintf(stderr, "ctx not given"); + return; + } + fprintf(stderr, "reindexing started"); + + + for (int i = 0; i < numaGetCount(ctx->classer->naclass); i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == oldIndex) { + numaSetValue(ctx->classer->naclass, i, newIndex); + } + } + fprintf(stderr, "reindexing successfull\n"); +} + +void printList(std::list &listToPrint) { + list::iterator printIt; + for (printIt = listToPrint.begin(); printIt != listToPrint.end(); printIt++) { + fprintf(stderr, "%d, ",(*printIt)); + } + fprintf(stderr, "\n"); +} + +/** + * unites templates of the same character to chosen charater template + * *ctx ............... structure containing templates of symbols + * targetChar ......... char that will remain (united char will be replaced by + * this char + * *charToBeUnited .... array of indexes to templates that should be replaced + * by targetCharTemplate + * + * n .................. number of templates to be united + * + * returns 0 on success and in error number different from zero + */ +// TODO: find out which is the first index and transfer to this position target char +int uniteTemplatesInTheList(struct jbig2ctx *ctx, int newRepresentant, list &templatesToBeUnited) { + if (!ctx) { + fprintf(stderr, "ctx not given"); + return 1; + } + if (templatesToBeUnited.empty()) { + fprintf(stderr, "given no templates for uniting"); + return 1; + } + +#ifdef UNIFICATION_DEBUGGING + fprintf(stderr, "Uniting templates to point to template %d:\n", newRepresentant); + printList(templatesToBeUnited); +#endif + + // check if newRepresentant exists + if ((newRepresentant < 0) || + (newRepresentant > pixaGetCount(ctx->classer->pixat))) { + fprintf(stderr, "new representant template out of range"); + return 1; + } + + list::iterator it; + for (it = templatesToBeUnited.begin(); it != templatesToBeUnited.end(); it++) { + + // first checking if the second template exists + int secondTemplate = (*it); + if ((secondTemplate < 0) || + (secondTemplate > pixaGetCount(ctx->classer->pixat))) { + fprintf(stderr, "template: %d out of range", (*it)); + return 1; + } + //reindexing(ctx, newRepresentant, secondTemplate); + for (int i = 0; i < ctx->classer->naclass->n; i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == secondTemplate) { + numaSetValue(ctx->classer->naclass, i, newRepresentant); + } + } + pixChangeRefcount(ctx->classer->pixat->pix[newRepresentant],pixGetRefcount(ctx->classer->pixat->pix[secondTemplate])); + } + return 0; +} + + + +/* + * ctx .... structure containing PIXA with templates + * list of templatesToRemove allready sorted + */ +int removeTemplates(jbig2ctx * ctx, std::list &templatesToRemove) { + if (!ctx) { + fprintf(stderr, "ctx not given\n"); + return 1; + } + if (templatesToRemove.empty()) { + fprintf(stderr, "given no templates to remove\n"); + return 0; + } + templatesToRemove.sort(); + +#ifdef UNIFICATION_DEBUGGING + fprintf(stderr, "Removing templates: "); + printList(templatesToRemove); +#endif + + std::list::iterator it; + it = templatesToRemove.begin(); + PIXA * pixat = ctx->classer->pixat; + + // index ... represents pointer to dictionary (PIXAT) and is processed in reverse + // it ... represents pointer to actual representant in list which should be removed + int last = templatesToRemove.back(); + for (int index = (pixat->n - 1); ((index >= (*it)) && (it != templatesToRemove.end())); index--) { + + // check if we assign PIX which should not be removed + if (index == last) { + templatesToRemove.pop_back(); + last = templatesToRemove.back(); + } else { + PIX * endPix; + PIX * coppiedPix; + BOXA * boxa; + int newIndex = (*it); + if (index != newIndex) { + endPix = ctx->classer->pixat->pix[index]; + coppiedPix = pixCopy(NULL, endPix); + boxa = ctx->classer->pixat->boxa; + l_int32 nbox = boxaGetCount(boxa); + BOX * box = NULL; + if (index < nbox) { + box = boxa->box[index]; + } + if (pixaReplacePix(ctx->classer->pixat, newIndex, coppiedPix, box)) { + fprintf(stderr, "uniting - unable to replace pix %d in pixat\n", newIndex); + return 2; + } + //reindexing(ctx, index, newIndex); + for (int i = 0; i < ctx->classer->naclass->n; i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == index) { + numaSetValue(ctx->classer->naclass, i, newIndex); + } + } + } + it++; + } + if (pixaRemovePix(ctx->classer->pixat, index)) { + fprintf(stderr, "uniting - unable to remove pix with index %d from pixat\n", index); + return 3; + } + ctx->classer->nclass--; + } + return 0; +} + +/** + * unites two templates to one template by reassigning indexes in numa struct and + * replacing deleted template by the last one + */ +int uniteTemplatesWithIndexes(struct jbig2ctx *ctx, int firstTemplateIndex, int secondTemplateIndex) { + if (!ctx) { + fprintf(stderr, "ctx doesn't exist"); + return 1; + } + + if ((ctx->classer->pixat->n < firstTemplateIndex) || (ctx->classer->pixat->n < secondTemplateIndex)) { + fprintf(stderr, "index doesn't point to templates array"); + return 1; + } + +/* + char firstbuf[128]; + sprintf(firstbuf, "uniting%dwith%d-first.png", firstTemplateIndex, secondTemplateIndex); + pixWrite(firstbuf, ctx->classer->pixat->pix[firstTemplateIndex], IFF_PNG); + + char secondbuf[128]; + sprintf(secondbuf, "uniting%dwith%d-second.png", firstTemplateIndex, secondTemplateIndex); + pixWrite(secondbuf, ctx->classer->pixat->pix[secondTemplateIndex], IFF_PNG); +*/ + + //reindexing(ctx, secondTemplateIndex, firstTemplateIndex); + for (int i = 0; i < ctx->classer->naclass->n; i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == secondTemplateIndex) { + numaSetValue(ctx->classer->naclass, i, firstTemplateIndex); + } + } + //reindexing(ctx, secondTemplateIndex, firstTemplateIndex); + for (int i = 0; i < ctx->classer->naclass->n; i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == secondTemplateIndex) { + numaSetValue(ctx->classer->naclass, i, firstTemplateIndex); + } + } + + pixChangeRefcount(ctx->classer->pixat->pix[firstTemplateIndex],pixGetRefcount(ctx->classer->pixat->pix[secondTemplateIndex])); + + + PIX * endPix; + PIX * coppiedPix; + BOXA * boxa; + int index = pixaGetCount(ctx->classer->pixat) - 1; + if (index != secondTemplateIndex) { + endPix = ctx->classer->pixat->pix[index]; + coppiedPix = pixCopy(NULL, endPix); + boxa = ctx->classer->pixat->boxa; + l_int32 nbox = boxaGetCount(boxa); + BOX * box = NULL; + if (index < nbox) { + box = boxa->box[index]; + } + if (pixaReplacePix(ctx->classer->pixat, secondTemplateIndex, coppiedPix, box)) { + fprintf(stderr, "uniting - unable to replace pix %d\n", secondTemplateIndex); + return 2; + } + for (int i = 0; i < ctx->classer->naclass->n; i++) { + int n; + numaGetIValue(ctx->classer->naclass, i, &n); + if (n == index) { + numaSetValue(ctx->classer->naclass, i, secondTemplateIndex); + } + } + + //reindexing(ctx, index, secondTemplateIndex); + } + + if (pixaRemovePix(ctx->classer->pixat, index)) { + fprintf(stderr, "uniting - unable to remove pix from pixat"); + return 3; + } + ctx->classer->nclass--; + + return 0; +} + +/** checks all PIXes in pixat (pixa of templates) if they are the same + * if they are the same it calls method that makes from them only one template + * and all indexes from second template are reindexed to the first one + */ +void autoThreshold(struct jbig2ctx *ctx) { + if (!ctx) { + fprintf(stderr, "jbig2ctx not given"); + return; + } +// fprintf(stderr, "autoThreshold used\n"); + PIXA *jbPixa = ctx->classer->pixat; + for (int i = 0; i < pixaGetCount(jbPixa); i++) { + PIX *jbPix = jbPixa->pix[i]; + + Result *result = new Result(jbPix); + for (int j = i+1; j < pixaGetCount(jbPixa); j++) { + //if (areEquivalent(jbPix, jbPixa->pix[j])) { + if (result->getDistance(new Result(jbPixa->pix[j])) < 0.5 ) { + uniteTemplatesWithIndexes(ctx, i, j); + j--; + } + } + } +} + + +void printHashMap(map > &hashedTemplates) { + std::map >::iterator it; + list::iterator itRepresentants; + for (it = hashedTemplates.begin(); it != hashedTemplates.end(); it++) { + fprintf(stderr, "for hash %d:\n", it->first); + fprintf(stderr, " -- "); + for (itRepresentants = it->second.begin(); itRepresentants != it->second.end(); itRepresentants++) { + fprintf(stderr, "%d ", (*itRepresentants)); + } + fprintf(stderr, "\n"); + } +} + +void countHash(PIX * pix, std::map > &hashMap, int templateIdx) { + if (!pix) { + fprintf(stderr, "no pix to count hash for\n"); + } + + //if (!hashMap) { + //fprintf(stderr, "missing pointer to hash map (table of hashes)"); + //} + + l_uint32 w = pixGetWidth(pix); + l_uint32 h = pixGetHeight(pix); + + //finding num of holes + l_int32 holes; + pixCountConnComp(pix, 4, &holes); + + unsigned int hash = (holes + 10 * h + 10000 * w) % 10000000; + + map >::iterator it; + it = hashMap.find(hash); + + if (it == hashMap.end()) { // creating new bin + it = hashMap.begin(); + list representants; + representants.push_back(templateIdx); + hashMap.insert(pair >(hash, representants)); + } else { // add to existing bin + it->second.push_back(templateIdx); + } +} + +/** checks all PIXes in pixat (pixa of templates) if they are the same + * if they are the same it calls method that makes from them only one template + * and all indexes from second template are reindexed to the first one + */ +void autoThresholdUsingHash(struct jbig2ctx *ctx) { + if (!ctx) { + fprintf(stderr, "jbig2ctx not given\n"); + return; + } + + std::map > hashedTemplates; + // creating hash value for each representant + PIXA *jbPixa = ctx->classer->pixat; + for (int i = 0; i < pixaGetCount(jbPixa); i++) { + countHash(jbPixa->pix[i], hashedTemplates, i); + } + map > newRepresentants; // where int is chosenOne and vector are old ones which should be replaced by chosenOne (united with it) + // going through representants with the same hash + std::map >::iterator it; + std::list::iterator itFirstTemplate; + std::list::iterator itSecondTemplate; + for (it = hashedTemplates.begin(); it != hashedTemplates.end(); it++) { + //comparing all the templates with same hash + for (itFirstTemplate = it->second.begin(); itFirstTemplate != it->second.end();) { + list templates; + templates.clear(); + itSecondTemplate = itFirstTemplate; + //fprintf(stderr, "firstTemplateIt: %d\n", (*itFirstTemplate)); + for (++itSecondTemplate; itSecondTemplate != it->second.end();) { + //fprintf(stderr, " -- itSecondTemplate: %d\n", (*itSecondTemplate)); + if (areEquivalent(jbPixa->pix[(*itFirstTemplate)], jbPixa->pix[(*itSecondTemplate)])) { +/* + fprintf(stderr, "Found PIXes which seems to be equivalent"); + printPix(jbPixa->pix[(*itFirstTemplate)]); + printPix(jbPixa->pix[(*itSecondTemplate)]); +*/ + // unite templates without removing (just reindexing) but add to array for later remove + templates.push_back((*itSecondTemplate)); + itSecondTemplate = (it->second.erase(itSecondTemplate)); + } else { + itSecondTemplate++; + } + } + if (!templates.empty()) { + newRepresentants.insert(pair >((*itFirstTemplate), templates)); + } + itFirstTemplate++; + } + } + list templatesToRemove; + list::iterator itRemove; + for (it = newRepresentants.begin(); it != newRepresentants.end(); it++) { + if (!uniteTemplatesInTheList(ctx, it->first, it->second)) { + templatesToRemove.merge(it->second); + //templatesToRemove.splice(templatesToRemove.begin(), it->second); + } + } + + if (removeTemplates(ctx, templatesToRemove)) { + fprintf(stderr, "warning: removing united templates wasn't fully succesfull"); + } + + //printHashMap(hashedTemplates); + //fprintf(stderr, "\n\n\n -------------NEW REPRESENTANTS-----------------\n\n"); + //printHashMap(newRepresentants); + +} + // see comments in .h file void jbig2_destroy(struct jbig2ctx *ctx) { diff --git a/src/jbig2enc.h b/src/jbig2enc.h index 8c0256c..b71a4ff 100644 --- a/src/jbig2enc.h +++ b/src/jbig2enc.h @@ -139,4 +139,21 @@ jbig2_encode_generic(struct Pix *const bw, const bool full_headers, const bool duplicate_line_removal, int *const length); +// ------------------------------------------------------------------------------- +// Checks all PIXes in pixat (pixa of templates) if they are the same +// if they are the same it calls method that makes from them only one template +// and all indexes from second template are reindexed to the first one +// +// requires implementation of functions +// uniteTemplates(struct jbig2ctx *ctx, int firstTemplate, int secondTemplate) +// areEquivalent(PIX *const firstTemplate, PIX *const secondTemplate) +// ------------------------------------------------------------------------------- +void autoThreshold(struct jbig2ctx *ctx); + +// ------------------------------------------------------------------------------- +// The same as autoThreshold, but it adds hash function in order to prevent +// unnecessary comparision and thus improve speed performance +// ------------------------------------------------------------------------------- +void autoThresholdUsingHash(struct jbig2ctx *ctx); + #endif // JBIG2ENC_JBIG2_H__ diff --git a/src/result.cc b/src/result.cc new file mode 100644 index 0000000..2af0aa5 --- /dev/null +++ b/src/result.cc @@ -0,0 +1,25 @@ +// Copyright 2006 Google Inc. All Rights Reserved. +// Author: hata.radim@gmail.com (Radim Hatlapatka) +// +// Copyright (C) 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "result.h" +#include "jbig2comparator.h" + +float Result::getDistance(Result *result) { + return 1 - areEquivalent(this->getPix(), result->getPix()); +} + + diff --git a/src/result.h b/src/result.h new file mode 100644 index 0000000..5c39a94 --- /dev/null +++ b/src/result.h @@ -0,0 +1,52 @@ +// Copyright 2006 Google Inc. All Rights Reserved. +// Author: hata.radim@gmail.com (Radim Hatlapatka) +// +// Copyright (C) 2012 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _JBIG2_RESULT_H_ +#define _JBIG2_RESULT_H_ + +#include + +/** + * Structure used as reference (fallback) point, which allows to count distance of two PIXes without using OCR + */ +class Result { + protected: + PIX * pix; + + public: + Result() {} + Result(PIX *pix) { + this->pix = pix; + } + + ~Result() { + this->pix = NULL; + } + + PIX * getPix() { + return pix; + } + + void setPix(PIX * pix) { + this->pix = pix; + } + + virtual float getDistance(Result *result); + +}; + +#endif diff --git a/vs2008/libjbig2enc/libjbig2enc.vcproj b/vs2008/libjbig2enc/libjbig2enc.vcproj index c7b99da..066339b 100644 --- a/vs2008/libjbig2enc/libjbig2enc.vcproj +++ b/vs2008/libjbig2enc/libjbig2enc.vcproj @@ -161,6 +161,12 @@ + + + + + +