diff --git a/ccutil/helpers.h b/ccutil/helpers.h index a913eb82b..480929c95 100644 --- a/ccutil/helpers.h +++ b/ccutil/helpers.h @@ -28,6 +28,49 @@ #include #include +#include "host.h" + +// TODO(rays) Put the rest of the helpers in the namespace. +namespace tesseract { + +// A simple linear congruential random number generator, using Knuth's +// constants from: +// http://en.wikipedia.org/wiki/Linear_congruential_generator. +class TRand { + public: + TRand() : seed_(1) {} + // Sets the seed to the given value. + void set_seed(uinT64 seed) { + seed_ = seed; + } + + // Returns an integer in the range 0 to MAX_INT32. + inT32 IntRand() { + Iterate(); + return seed_ >> 33; + } + // Returns a floating point value in the range [-range, range]. + double SignedRand(double range) { + return range * 2.0 * IntRand() / MAX_INT32 - range; + } + // Returns a floating point value in the range [0, range]. + double UnsignedRand(double range) { + return range * IntRand() / MAX_INT32; + } + + private: + // Steps the generator to the next value. + void Iterate() { + seed_ *= 6364136223846793005; + seed_ += 1442695040888963407; + } + + // The current value of the seed. + uinT64 seed_; +}; + +} // namespace tesseract + // Remove newline (if any) at the end of the string. inline void chomp_string(char *str) { int last_index = strlen(str) - 1; diff --git a/training/degradeimage.cpp b/training/degradeimage.cpp index f7fe06303..79f1da5d0 100644 --- a/training/degradeimage.cpp +++ b/training/degradeimage.cpp @@ -22,6 +22,7 @@ #include #include "allheaders.h" // from leptonica +#include "helpers.h" // For TRand. namespace tesseract { @@ -34,17 +35,13 @@ const int kSaltnPepper = 5; // Min sum of width + height on which to operate the ramp. const int kMinRampSize = 1000; -static unsigned int random_seed = 0x18273645; -#ifndef rand_r // _MSC_VER, ANDROID -#define rand_r(random_seed) rand() -#endif // _MSC_VER - // Degrade the pix as if by a print/copy/scan cycle with exposure > 0 // corresponding to darkening on the copier and <0 lighter and 0 not copied. // Exposures in [-2,2] are most useful, with -3 and 3 being extreme. // If rotation is NULL, rotation is skipped. If *rotation is non-zero, the pix // is rotated by *rotation else it is randomly rotated and *rotation is // modified. +// // HOW IT WORKS: // Most of the process is really dictated by the fact that the minimum // available convolution is 3X3, which is too big really to simulate a @@ -65,7 +62,8 @@ static unsigned int random_seed = 0x18273645; // the edges. // Finally a greyscale ramp provides a continuum of effects between exposure // levels. -Pix* DegradeImage(Pix* input, int exposure, float* rotation) { +Pix* DegradeImage(Pix* input, int exposure, TRand* randomizer, + float* rotation) { Pix* pix = pixConvertTo8(input, false); pixDestroy(&input); input = pix; @@ -85,12 +83,11 @@ Pix* DegradeImage(Pix* input, int exposure, float* rotation) { pixDestroy(&input); // A small random rotation helps to make the edges jaggy in a realistic way. if (rotation != NULL) { - float radians_clockwise; + float radians_clockwise = 0.0f; if (*rotation) { radians_clockwise = *rotation; - } else { - radians_clockwise = (2.0*rand_r(&random_seed)/RAND_MAX - 1.0) * - kRotationRange; + } else if (randomizer != NULL) { + radians_clockwise = randomizer->SignedRand(kRotationRange); } input = pixRotate(pix, radians_clockwise, @@ -131,7 +128,8 @@ Pix* DegradeImage(Pix* input, int exposure, float* rotation) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int pixel = GET_DATA_BYTE(data, x); - pixel += rand_r(&random_seed) % (kSaltnPepper*2 + 1) - kSaltnPepper; + if (randomizer != NULL) + pixel += randomizer->IntRand() % (kSaltnPepper*2 + 1) - kSaltnPepper; if (height + width > kMinRampSize) pixel -= (2*x + y) * 32 / (height + width); pixel += erosion_offset; diff --git a/training/degradeimage.h b/training/degradeimage.h index 189494ca0..2add6282f 100644 --- a/training/degradeimage.h +++ b/training/degradeimage.h @@ -24,12 +24,15 @@ struct Pix; namespace tesseract { +class TRand; + // Degrade the pix as if by a print/copy/scan cycle with exposure > 0 // corresponding to darkening on the copier and <0 lighter and 0 not copied. // If rotation is not NULL, the clockwise rotation in radians is saved there. // The input pix must be 8 bit grey. (Binary with values 0 and 255 is OK.) // The input image is destroyed and a different image returned. -struct Pix* DegradeImage(struct Pix* input, int exposure, float* rotation); +struct Pix* DegradeImage(struct Pix* input, int exposure, TRand* randomizer, + float* rotation); } // namespace tesseract diff --git a/training/text2image.cpp b/training/text2image.cpp index 145adfced..438849405 100644 --- a/training/text2image.cpp +++ b/training/text2image.cpp @@ -43,6 +43,7 @@ #include "degradeimage.h" #include "errcode.h" #include "fileio.h" +#include "helpers.h" #include "normstrngs.h" #include "stringrenderer.h" #include "tlog.h" @@ -55,6 +56,9 @@ using std::map; using std::pair; #endif +// A number with which to initialize the random number generator. +const int kRandomSeed = 0x18273645; + // The text input file. STRING_PARAM_FLAG(text, "", "File name of text input to process"); @@ -534,6 +538,8 @@ int main(int argc, char** argv) { vector page_rotation; const char* to_render_utf8 = src_utf8.c_str(); + tesseract::TRand randomizer; + randomizer.set_seed(kRandomSeed); // We use a two pass mechanism to rotate images in both direction. // The first pass(0) will rotate the images in random directions and // the second pass(1) will mirror those rotations. @@ -560,7 +566,7 @@ int main(int argc, char** argv) { rotation = -1 * page_rotation[page_num]; } if (FLAGS_degrade_image) { - pix = DegradeImage(pix, FLAGS_exposure, &rotation); + pix = DegradeImage(pix, FLAGS_exposure, &randomizer, &rotation); } render.RotatePageBoxes(rotation);