tesseract  4.00.00dev
imagedata.cpp
Go to the documentation of this file.
1 // File: imagedata.cpp
3 // Description: Class to hold information about a single multi-page tiff
4 // training file and its corresponding boxes or text file.
5 // Author: Ray Smith
6 // Created: Tue May 28 08:56:06 PST 2013
7 //
8 // (C) Copyright 2013, Google Inc.
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
19 
20 // Include automatically generated configuration file if running autoconf.
21 #ifdef HAVE_CONFIG_H
22 #include "config_auto.h"
23 #endif
24 
25 #include "imagedata.h"
26 
27 #if defined(__MINGW32__)
28 #include <unistd.h>
29 #else
30 #include <thread>
31 #endif
32 
33 #include "allheaders.h"
34 #include "boxread.h"
35 #include "callcpp.h"
36 #include "helpers.h"
37 #include "tprintf.h"
38 
39 // Number of documents to read ahead while training. Doesn't need to be very
40 // large.
41 const int kMaxReadAhead = 8;
42 
43 namespace tesseract {
44 
45 WordFeature::WordFeature() : x_(0), y_(0), dir_(0) {
46 }
47 
49  : x_(IntCastRounded(fcoord.x())),
50  y_(ClipToRange(IntCastRounded(fcoord.y()), 0, MAX_UINT8)),
51  dir_(dir) {
52 }
53 
54 // Computes the maximum x and y value in the features.
56  int* max_x, int* max_y) {
57  *max_x = 0;
58  *max_y = 0;
59  for (int f = 0; f < features.size(); ++f) {
60  if (features[f].x_ > *max_x) *max_x = features[f].x_;
61  if (features[f].y_ > *max_y) *max_y = features[f].y_;
62  }
63 }
64 
65 // Draws the features in the given window.
67  ScrollView* window) {
68 #ifndef GRAPHICS_DISABLED
69  for (int f = 0; f < features.size(); ++f) {
70  FCOORD pos(features[f].x_, features[f].y_);
71  FCOORD dir;
72  dir.from_direction(features[f].dir_);
73  dir *= 8.0f;
74  window->SetCursor(IntCastRounded(pos.x() - dir.x()),
75  IntCastRounded(pos.y() - dir.y()));
76  window->DrawTo(IntCastRounded(pos.x() + dir.x()),
77  IntCastRounded(pos.y() + dir.y()));
78  }
79 #endif
80 }
81 
82 // Writes to the given file. Returns false in case of error.
83 bool WordFeature::Serialize(FILE* fp) const {
84  if (fwrite(&x_, sizeof(x_), 1, fp) != 1) return false;
85  if (fwrite(&y_, sizeof(y_), 1, fp) != 1) return false;
86  if (fwrite(&dir_, sizeof(dir_), 1, fp) != 1) return false;
87  return true;
88 }
89 // Reads from the given file. Returns false in case of error.
90 // If swap is true, assumes a big/little-endian swap is needed.
91 bool WordFeature::DeSerialize(bool swap, FILE* fp) {
92  if (fread(&x_, sizeof(x_), 1, fp) != 1) return false;
93  if (swap) ReverseN(&x_, sizeof(x_));
94  if (fread(&y_, sizeof(y_), 1, fp) != 1) return false;
95  if (fread(&dir_, sizeof(dir_), 1, fp) != 1) return false;
96  return true;
97 }
98 
100  const GenericVector<WordFeature>& word_features,
101  GenericVector<FloatWordFeature>* float_features) {
102  for (int i = 0; i < word_features.size(); ++i) {
104  f.x = word_features[i].x();
105  f.y = word_features[i].y();
106  f.dir = word_features[i].dir();
107  f.x_bucket = 0; // Will set it later.
108  float_features->push_back(f);
109  }
110 }
111 
112 // Sort function to sort first by x-bucket, then by y.
113 /* static */
114 int FloatWordFeature::SortByXBucket(const void* v1, const void* v2) {
115  const FloatWordFeature* f1 = static_cast<const FloatWordFeature*>(v1);
116  const FloatWordFeature* f2 = static_cast<const FloatWordFeature*>(v2);
117  int x_diff = f1->x_bucket - f2->x_bucket;
118  if (x_diff == 0) return f1->y - f2->y;
119  return x_diff;
120 }
121 
122 ImageData::ImageData() : page_number_(-1), vertical_text_(false) {
123 }
124 // Takes ownership of the pix and destroys it.
125 ImageData::ImageData(bool vertical, Pix* pix)
126  : page_number_(0), vertical_text_(vertical) {
127  SetPix(pix);
128 }
130 }
131 
132 // Builds and returns an ImageData from the basic data. Note that imagedata,
133 // truth_text, and box_text are all the actual file data, NOT filenames.
134 ImageData* ImageData::Build(const char* name, int page_number, const char* lang,
135  const char* imagedata, int imagedatasize,
136  const char* truth_text, const char* box_text) {
137  ImageData* image_data = new ImageData();
138  image_data->imagefilename_ = name;
139  image_data->page_number_ = page_number;
140  image_data->language_ = lang;
141  // Save the imagedata.
142  image_data->image_data_.resize_no_init(imagedatasize);
143  memcpy(&image_data->image_data_[0], imagedata, imagedatasize);
144  if (!image_data->AddBoxes(box_text)) {
145  if (truth_text == NULL || truth_text[0] == '\0') {
146  tprintf("Error: No text corresponding to page %d from image %s!\n",
147  page_number, name);
148  delete image_data;
149  return NULL;
150  }
151  image_data->transcription_ = truth_text;
152  // If we have no boxes, the transcription is in the 0th box_texts_.
153  image_data->box_texts_.push_back(truth_text);
154  // We will create a box for the whole image on PreScale, to save unpacking
155  // the image now.
156  } else if (truth_text != NULL && truth_text[0] != '\0' &&
157  image_data->transcription_ != truth_text) {
158  // Save the truth text as it is present and disagrees with the box text.
159  image_data->transcription_ = truth_text;
160  }
161  return image_data;
162 }
163 
164 // Writes to the given file. Returns false in case of error.
165 bool ImageData::Serialize(TFile* fp) const {
166  if (!imagefilename_.Serialize(fp)) return false;
167  if (fp->FWrite(&page_number_, sizeof(page_number_), 1) != 1) return false;
168  if (!image_data_.Serialize(fp)) return false;
169  if (!language_.Serialize(fp)) return false;
170  if (!transcription_.Serialize(fp)) return false;
171  // WARNING: Will not work across different endian machines.
172  if (!boxes_.Serialize(fp)) return false;
173  if (!box_texts_.SerializeClasses(fp)) return false;
174  inT8 vertical = vertical_text_;
175  if (fp->FWrite(&vertical, sizeof(vertical), 1) != 1) return false;
176  return true;
177 }
178 
179 // Reads from the given file. Returns false in case of error.
180 // If swap is true, assumes a big/little-endian swap is needed.
182  if (!imagefilename_.DeSerialize(fp)) return false;
183  if (fp->FReadEndian(&page_number_, sizeof(page_number_), 1) != 1)
184  return false;
185  if (!image_data_.DeSerialize(fp)) return false;
186  if (!language_.DeSerialize(fp)) return false;
187  if (!transcription_.DeSerialize(fp)) return false;
188  // WARNING: Will not work across different endian machines.
189  if (!boxes_.DeSerialize(fp)) return false;
190  if (!box_texts_.DeSerializeClasses(fp)) return false;
191  inT8 vertical = 0;
192  if (fp->FRead(&vertical, sizeof(vertical), 1) != 1) return false;
193  vertical_text_ = vertical != 0;
194  return true;
195 }
196 
197 // As DeSerialize, but only seeks past the data - hence a static method.
199  if (!STRING::SkipDeSerialize(fp)) return false;
201  if (fp->FRead(&page_number, sizeof(page_number), 1) != 1) return false;
202  if (!GenericVector<char>::SkipDeSerialize(fp)) return false;
203  if (!STRING::SkipDeSerialize(fp)) return false;
204  if (!STRING::SkipDeSerialize(fp)) return false;
205  if (!GenericVector<TBOX>::SkipDeSerialize(fp)) return false;
206  if (!GenericVector<STRING>::SkipDeSerializeClasses(fp)) return false;
207  inT8 vertical = 0;
208  return fp->FRead(&vertical, sizeof(vertical), 1) == 1;
209 }
210 
211 // Saves the given Pix as a PNG-encoded string and destroys it.
212 void ImageData::SetPix(Pix* pix) {
213  SetPixInternal(pix, &image_data_);
214 }
215 
216 // Returns the Pix image for *this. Must be pixDestroyed after use.
217 Pix* ImageData::GetPix() const {
218  return GetPixInternal(image_data_);
219 }
220 
221 // Gets anything and everything with a non-NULL pointer, prescaled to a
222 // given target_height (if 0, then the original image height), and aligned.
223 // Also returns (if not NULL) the width and height of the scaled image.
224 // The return value is the scaled Pix, which must be pixDestroyed after use,
225 // and scale_factor (if not NULL) is set to the scale factor that was applied
226 // to the image to achieve the target_height.
227 Pix* ImageData::PreScale(int target_height, int max_height, float* scale_factor,
228  int* scaled_width, int* scaled_height,
229  GenericVector<TBOX>* boxes) const {
230  int input_width = 0;
231  int input_height = 0;
232  Pix* src_pix = GetPix();
233  ASSERT_HOST(src_pix != NULL);
234  input_width = pixGetWidth(src_pix);
235  input_height = pixGetHeight(src_pix);
236  if (target_height == 0) {
237  target_height = MIN(input_height, max_height);
238  }
239  float im_factor = static_cast<float>(target_height) / input_height;
240  if (scaled_width != NULL)
241  *scaled_width = IntCastRounded(im_factor * input_width);
242  if (scaled_height != NULL)
243  *scaled_height = target_height;
244  // Get the scaled image.
245  Pix* pix = pixScale(src_pix, im_factor, im_factor);
246  if (pix == NULL) {
247  tprintf("Scaling pix of size %d, %d by factor %g made null pix!!\n",
248  input_width, input_height, im_factor);
249  }
250  if (scaled_width != NULL) *scaled_width = pixGetWidth(pix);
251  if (scaled_height != NULL) *scaled_height = pixGetHeight(pix);
252  pixDestroy(&src_pix);
253  if (boxes != NULL) {
254  // Get the boxes.
255  boxes->truncate(0);
256  for (int b = 0; b < boxes_.size(); ++b) {
257  TBOX box = boxes_[b];
258  box.scale(im_factor);
259  boxes->push_back(box);
260  }
261  if (boxes->empty()) {
262  // Make a single box for the whole image.
263  TBOX box(0, 0, im_factor * input_width, target_height);
264  boxes->push_back(box);
265  }
266  }
267  if (scale_factor != NULL) *scale_factor = im_factor;
268  return pix;
269 }
270 
272  return image_data_.size();
273 }
274 
275 // Draws the data in a new window.
276 void ImageData::Display() const {
277 #ifndef GRAPHICS_DISABLED
278  const int kTextSize = 64;
279  // Draw the image.
280  Pix* pix = GetPix();
281  if (pix == NULL) return;
282  int width = pixGetWidth(pix);
283  int height = pixGetHeight(pix);
284  ScrollView* win = new ScrollView("Imagedata", 100, 100,
285  2 * (width + 2 * kTextSize),
286  2 * (height + 4 * kTextSize),
287  width + 10, height + 3 * kTextSize, true);
288  win->Image(pix, 0, height - 1);
289  pixDestroy(&pix);
290  // Draw the boxes.
291  win->Pen(ScrollView::RED);
292  win->Brush(ScrollView::NONE);
293  int text_size = kTextSize;
294  if (!boxes_.empty() && boxes_[0].height() * 2 < text_size)
295  text_size = boxes_[0].height() * 2;
296  win->TextAttributes("Arial", text_size, false, false, false);
297  if (!boxes_.empty()) {
298  for (int b = 0; b < boxes_.size(); ++b) {
299  boxes_[b].plot(win);
300  win->Text(boxes_[b].left(), height + kTextSize, box_texts_[b].string());
301  }
302  } else {
303  // The full transcription.
304  win->Pen(ScrollView::CYAN);
305  win->Text(0, height + kTextSize * 2, transcription_.string());
306  }
307  win->Update();
308  window_wait(win);
309 #endif
310 }
311 
312 // Adds the supplied boxes and transcriptions that correspond to the correct
313 // page number.
315  const GenericVector<STRING>& texts,
316  const GenericVector<int>& box_pages) {
317  // Copy the boxes and make the transcription.
318  for (int i = 0; i < box_pages.size(); ++i) {
319  if (page_number_ >= 0 && box_pages[i] != page_number_) continue;
320  transcription_ += texts[i];
321  boxes_.push_back(boxes[i]);
322  box_texts_.push_back(texts[i]);
323  }
324 }
325 
326 // Saves the given Pix as a PNG-encoded string and destroys it.
327 void ImageData::SetPixInternal(Pix* pix, GenericVector<char>* image_data) {
328  l_uint8* data;
329  size_t size;
330  pixWriteMem(&data, &size, pix, IFF_PNG);
331  pixDestroy(&pix);
332  image_data->resize_no_init(size);
333  memcpy(&(*image_data)[0], data, size);
334  lept_free(data);
335 }
336 
337 // Returns the Pix image for the image_data. Must be pixDestroyed after use.
338 Pix* ImageData::GetPixInternal(const GenericVector<char>& image_data) {
339  Pix* pix = NULL;
340  if (!image_data.empty()) {
341  // Convert the array to an image.
342  const unsigned char* u_data =
343  reinterpret_cast<const unsigned char*>(&image_data[0]);
344  pix = pixReadMem(u_data, image_data.size());
345  }
346  return pix;
347 }
348 
349 // Parses the text string as a box file and adds any discovered boxes that
350 // match the page number. Returns false on error.
351 bool ImageData::AddBoxes(const char* box_text) {
352  if (box_text != NULL && box_text[0] != '\0') {
354  GenericVector<STRING> texts;
355  GenericVector<int> box_pages;
356  if (ReadMemBoxes(page_number_, /*skip_blanks*/ false, box_text,
357  /*continue_on_failure*/ true, &boxes, &texts, NULL,
358  &box_pages)) {
359  AddBoxes(boxes, texts, box_pages);
360  return true;
361  } else {
362  tprintf("Error: No boxes for page %d from image %s!\n",
363  page_number_, imagefilename_.string());
364  }
365  }
366  return false;
367 }
368 
369 // Thread function to call ReCachePages.
370 void* ReCachePagesFunc(void* data) {
371  DocumentData* document_data = static_cast<DocumentData*>(data);
372  document_data->ReCachePages();
373  return NULL;
374 }
375 
377  : document_name_(name),
378  pages_offset_(-1),
379  total_pages_(-1),
380  memory_used_(0),
381  max_memory_(0),
382  reader_(NULL) {}
383 
385  SVAutoLock lock_p(&pages_mutex_);
386  SVAutoLock lock_g(&general_mutex_);
387 }
388 
389 // Reads all the pages in the given lstmf filename to the cache. The reader
390 // is used to read the file.
391 bool DocumentData::LoadDocument(const char* filename, int start_page,
392  inT64 max_memory, FileReader reader) {
393  SetDocument(filename, max_memory, reader);
394  pages_offset_ = start_page;
395  return ReCachePages();
396 }
397 
398 // Sets up the document, without actually loading it.
399 void DocumentData::SetDocument(const char* filename, inT64 max_memory,
400  FileReader reader) {
401  SVAutoLock lock_p(&pages_mutex_);
402  SVAutoLock lock(&general_mutex_);
403  document_name_ = filename;
404  pages_offset_ = -1;
405  max_memory_ = max_memory;
406  reader_ = reader;
407 }
408 
409 // Writes all the pages to the given filename. Returns false on error.
410 bool DocumentData::SaveDocument(const char* filename, FileWriter writer) {
411  SVAutoLock lock(&pages_mutex_);
412  TFile fp;
413  fp.OpenWrite(NULL);
414  if (!pages_.Serialize(&fp) || !fp.CloseWrite(filename, writer)) {
415  tprintf("Serialize failed: %s\n", filename);
416  return false;
417  }
418  return true;
419 }
421  SVAutoLock lock(&pages_mutex_);
422  TFile fp;
423  fp.OpenWrite(buffer);
424  return pages_.Serialize(&fp);
425 }
426 
427 // Adds the given page data to this document, counting up memory.
429  SVAutoLock lock(&pages_mutex_);
430  pages_.push_back(page);
431  set_memory_used(memory_used() + page->MemoryUsed());
432 }
433 
434 // If the given index is not currently loaded, loads it using a separate
435 // thread.
437  ImageData* page = NULL;
438  if (IsPageAvailable(index, &page)) return;
439  SVAutoLock lock(&pages_mutex_);
440  if (pages_offset_ == index) return;
441  pages_offset_ = index;
442  pages_.clear();
444 }
445 
446 // Returns a pointer to the page with the given index, modulo the total
447 // number of pages. Blocks until the background load is completed.
448 const ImageData* DocumentData::GetPage(int index) {
449  ImageData* page = NULL;
450  while (!IsPageAvailable(index, &page)) {
451  // If there is no background load scheduled, schedule one now.
452  pages_mutex_.Lock();
453  bool needs_loading = pages_offset_ != index;
454  pages_mutex_.Unlock();
455  if (needs_loading) LoadPageInBackground(index);
456  // We can't directly load the page, or the background load will delete it
457  // while the caller is using it, so give it a chance to work.
458 #if defined(__MINGW32__)
459  sleep(1);
460 #else
461  std::this_thread::sleep_for(std::chrono::seconds(1));
462 #endif
463  }
464  return page;
465 }
466 
467 // Returns true if the requested page is available, and provides a pointer,
468 // which may be NULL if the document is empty. May block, even though it
469 // doesn't guarantee to return true.
470 bool DocumentData::IsPageAvailable(int index, ImageData** page) {
471  SVAutoLock lock(&pages_mutex_);
472  int num_pages = NumPages();
473  if (num_pages == 0 || index < 0) {
474  *page = NULL; // Empty Document.
475  return true;
476  }
477  if (num_pages > 0) {
478  index = Modulo(index, num_pages);
479  if (pages_offset_ <= index && index < pages_offset_ + pages_.size()) {
480  *page = pages_[index - pages_offset_]; // Page is available already.
481  return true;
482  }
483  }
484  return false;
485 }
486 
487 // Removes all pages from memory and frees the memory, but does not forget
488 // the document metadata.
490  SVAutoLock lock(&pages_mutex_);
491  inT64 memory_saved = memory_used();
492  pages_.clear();
493  pages_offset_ = -1;
494  set_total_pages(-1);
495  set_memory_used(0);
496  tprintf("Unloaded document %s, saving %" PRId64 " memory\n",
497  document_name_.string(), memory_saved);
498  return memory_saved;
499 }
500 
501 // Shuffles all the pages in the document.
503  TRand random;
504  // Different documents get shuffled differently, but the same for the same
505  // name.
506  random.set_seed(document_name_.string());
507  int num_pages = pages_.size();
508  // Execute one random swap for each page in the document.
509  for (int i = 0; i < num_pages; ++i) {
510  int src = random.IntRand() % num_pages;
511  int dest = random.IntRand() % num_pages;
512  std::swap(pages_[src], pages_[dest]);
513  }
514 }
515 
516 // Locks the pages_mutex_ and Loads as many pages can fit in max_memory_
517 // starting at index pages_offset_.
518 bool DocumentData::ReCachePages() {
519  SVAutoLock lock(&pages_mutex_);
520  // Read the file.
521  set_total_pages(0);
522  set_memory_used(0);
523  int loaded_pages = 0;
524  pages_.truncate(0);
525  TFile fp;
526  if (!fp.Open(document_name_, reader_) ||
527  !PointerVector<ImageData>::DeSerializeSize(&fp, &loaded_pages) ||
528  loaded_pages <= 0) {
529  tprintf("Deserialize header failed: %s\n", document_name_.string());
530  return false;
531  }
532  pages_offset_ %= loaded_pages;
533  // Skip pages before the first one we want, and load the rest until max
534  // memory and skip the rest after that.
535  int page;
536  for (page = 0; page < loaded_pages; ++page) {
537  if (page < pages_offset_ ||
538  (max_memory_ > 0 && memory_used() > max_memory_)) {
540  tprintf("Deserializeskip failed\n");
541  break;
542  }
543  } else {
544  if (!pages_.DeSerializeElement(&fp)) break;
545  ImageData* image_data = pages_.back();
546  if (image_data->imagefilename().length() == 0) {
547  image_data->set_imagefilename(document_name_);
548  image_data->set_page_number(page);
549  }
550  set_memory_used(memory_used() + image_data->MemoryUsed());
551  }
552  }
553  if (page < loaded_pages) {
554  tprintf("Deserialize failed: %s read %d/%d pages\n",
555  document_name_.string(), page, loaded_pages);
556  pages_.truncate(0);
557  } else {
558  tprintf("Loaded %d/%d pages (%d-%d) of document %s\n", pages_.size(),
559  loaded_pages, pages_offset_ + 1, pages_offset_ + pages_.size(),
560  document_name_.string());
561  }
562  set_total_pages(loaded_pages);
563  return !pages_.empty();
564 }
565 
566 // A collection of DocumentData that knows roughly how much memory it is using.
568  : num_pages_per_doc_(0), max_memory_(max_memory) {}
570 
571 // Adds all the documents in the list of filenames, counting memory.
572 // The reader is used to read the files.
574  CachingStrategy cache_strategy,
575  FileReader reader) {
576  cache_strategy_ = cache_strategy;
577  inT64 fair_share_memory = 0;
578  // In the round-robin case, each DocumentData handles restricting its content
579  // to its fair share of memory. In the sequential case, DocumentCache
580  // determines which DocumentDatas are held entirely in memory.
581  if (cache_strategy_ == CS_ROUND_ROBIN)
582  fair_share_memory = max_memory_ / filenames.size();
583  for (int arg = 0; arg < filenames.size(); ++arg) {
584  STRING filename = filenames[arg];
585  DocumentData* document = new DocumentData(filename);
586  document->SetDocument(filename.string(), fair_share_memory, reader);
587  AddToCache(document);
588  }
589  if (!documents_.empty()) {
590  // Try to get the first page now to verify the list of filenames.
591  if (GetPageBySerial(0) != NULL) return true;
592  tprintf("Load of page 0 failed!\n");
593  }
594  return false;
595 }
596 
597 // Adds document to the cache.
599  documents_.push_back(data);
600  return true;
601 }
602 
603 // Finds and returns a document by name.
604 DocumentData* DocumentCache::FindDocument(const STRING& document_name) const {
605  for (int i = 0; i < documents_.size(); ++i) {
606  if (documents_[i]->document_name() == document_name)
607  return documents_[i];
608  }
609  return NULL;
610 }
611 
612 // Returns the total number of pages in an epoch. For CS_ROUND_ROBIN cache
613 // strategy, could take a long time.
615  if (cache_strategy_ == CS_SEQUENTIAL) {
616  // In sequential mode, we assume each doc has the same number of pages
617  // whether it is true or not.
618  if (num_pages_per_doc_ == 0) GetPageSequential(0);
619  return num_pages_per_doc_ * documents_.size();
620  }
621  int total_pages = 0;
622  int num_docs = documents_.size();
623  for (int d = 0; d < num_docs; ++d) {
624  // We have to load a page to make NumPages() valid.
625  documents_[d]->GetPage(0);
626  total_pages += documents_[d]->NumPages();
627  }
628  return total_pages;
629 }
630 
631 // Returns a page by serial number, selecting them in a round-robin fashion
632 // from all the documents. Highly disk-intensive, but doesn't need samples
633 // to be shuffled between files to begin with.
634 const ImageData* DocumentCache::GetPageRoundRobin(int serial) {
635  int num_docs = documents_.size();
636  int doc_index = serial % num_docs;
637  const ImageData* doc = documents_[doc_index]->GetPage(serial / num_docs);
638  for (int offset = 1; offset <= kMaxReadAhead && offset < num_docs; ++offset) {
639  doc_index = (serial + offset) % num_docs;
640  int page = (serial + offset) / num_docs;
641  documents_[doc_index]->LoadPageInBackground(page);
642  }
643  return doc;
644 }
645 
646 // Returns a page by serial number, selecting them in sequence from each file.
647 // Requires the samples to be shuffled between the files to give a random or
648 // uniform distribution of data. Less disk-intensive than GetPageRoundRobin.
649 const ImageData* DocumentCache::GetPageSequential(int serial) {
650  int num_docs = documents_.size();
651  ASSERT_HOST(num_docs > 0);
652  if (num_pages_per_doc_ == 0) {
653  // Use the pages in the first doc as the number of pages in each doc.
654  documents_[0]->GetPage(0);
655  num_pages_per_doc_ = documents_[0]->NumPages();
656  if (num_pages_per_doc_ == 0) {
657  tprintf("First document cannot be empty!!\n");
658  ASSERT_HOST(num_pages_per_doc_ > 0);
659  }
660  // Get rid of zero now if we don't need it.
661  if (serial / num_pages_per_doc_ % num_docs > 0) documents_[0]->UnCache();
662  }
663  int doc_index = serial / num_pages_per_doc_ % num_docs;
664  const ImageData* doc =
665  documents_[doc_index]->GetPage(serial % num_pages_per_doc_);
666  // Count up total memory. Background loading makes it more complicated to
667  // keep a running count.
668  inT64 total_memory = 0;
669  for (int d = 0; d < num_docs; ++d) {
670  total_memory += documents_[d]->memory_used();
671  }
672  if (total_memory >= max_memory_) {
673  // Find something to un-cache.
674  // If there are more than 3 in front, then serial is from the back reader
675  // of a pair of readers. If we un-cache from in-front-2 to 2-ahead, then
676  // we create a hole between them and then un-caching the backmost occupied
677  // will work for both.
678  int num_in_front = CountNeighbourDocs(doc_index, 1);
679  for (int offset = num_in_front - 2;
680  offset > 1 && total_memory >= max_memory_; --offset) {
681  int next_index = (doc_index + offset) % num_docs;
682  total_memory -= documents_[next_index]->UnCache();
683  }
684  // If that didn't work, the best solution is to un-cache from the back. If
685  // we take away the document that a 2nd reader is using, it will put it
686  // back and make a hole between.
687  int num_behind = CountNeighbourDocs(doc_index, -1);
688  for (int offset = num_behind; offset < 0 && total_memory >= max_memory_;
689  ++offset) {
690  int next_index = (doc_index + offset + num_docs) % num_docs;
691  total_memory -= documents_[next_index]->UnCache();
692  }
693  }
694  int next_index = (doc_index + 1) % num_docs;
695  if (!documents_[next_index]->IsCached() && total_memory < max_memory_) {
696  documents_[next_index]->LoadPageInBackground(0);
697  }
698  return doc;
699 }
700 
701 // Helper counts the number of adjacent cached neighbours of index looking in
702 // direction dir, ie index+dir, index+2*dir etc.
703 int DocumentCache::CountNeighbourDocs(int index, int dir) {
704  int num_docs = documents_.size();
705  for (int offset = dir; abs(offset) < num_docs; offset += dir) {
706  int offset_index = (index + offset + num_docs) % num_docs;
707  if (!documents_[offset_index]->IsCached()) return offset - dir;
708  }
709  return num_docs;
710 }
711 
712 } // namespace tesseract.
Definition: points.h:189
bool empty() const
Definition: genericvector.h:91
#define MIN(x, y)
Definition: ndminx.h:28
static bool DeSerializeSize(TFile *fp, inT32 *size)
int FWrite(const void *buffer, int size, int count)
Definition: serialis.cpp:148
bool IsPageAvailable(int index, ImageData **page)
Definition: imagedata.cpp:470
static bool SkipDeSerialize(tesseract::TFile *fp)
Definition: strngs.cpp:183
float y() const
Definition: points.h:212
void Lock()
Locks on a mutex.
Definition: svutil.cpp:70
void DrawTo(int x, int y)
Definition: scrollview.cpp:531
static int SortByXBucket(const void *, const void *)
Definition: imagedata.cpp:114
void Unlock()
Unlocks on a mutex.
Definition: svutil.cpp:78
bool AddToCache(DocumentData *data)
Definition: imagedata.cpp:598
void OpenWrite(GenericVector< char > *data)
Definition: serialis.cpp:125
void scale(const float f)
Definition: rect.h:171
const STRING & imagefilename() const
Definition: imagedata.h:124
bool LoadDocument(const char *filename, int start_page, inT64 max_memory, FileReader reader)
Definition: imagedata.cpp:391
bool LoadDocuments(const GenericVector< STRING > &filenames, CachingStrategy cache_strategy, FileReader reader)
Definition: imagedata.cpp:573
void SetPix(Pix *pix)
Definition: imagedata.cpp:212
static void Update()
Definition: scrollview.cpp:715
const ImageData * GetPageBySerial(int serial)
Definition: imagedata.h:335
void resize_no_init(int size)
Definition: genericvector.h:66
void set_page_number(int num)
Definition: imagedata.h:133
bool SaveToBuffer(GenericVector< char > *buffer)
Definition: imagedata.cpp:420
void set_imagefilename(const STRING &name)
Definition: imagedata.h:127
inT64 memory_used() const
Definition: imagedata.h:231
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:122
int size() const
Definition: genericvector.h:72
#define MAX_UINT8
Definition: host.h:63
void set_seed(uinT64 seed)
Definition: helpers.h:45
CachingStrategy
Definition: imagedata.h:40
void AddBoxes(const GenericVector< TBOX > &boxes, const GenericVector< STRING > &texts, const GenericVector< int > &box_pages)
Definition: imagedata.cpp:314
static void Draw(const GenericVector< WordFeature > &features, ScrollView *window)
Definition: imagedata.cpp:66
#define tprintf(...)
Definition: tprintf.h:31
const GenericVector< char > & image_data() const
Definition: imagedata.h:136
uint8_t uinT8
Definition: host.h:35
void truncate(int size)
bool(* FileWriter)(const GenericVector< char > &data, const STRING &filename)
void Brush(Color color)
Definition: scrollview.cpp:732
int8_t inT8
Definition: host.h:34
static bool SkipDeSerialize(tesseract::TFile *fp)
Definition: imagedata.cpp:198
void Pen(Color color)
Definition: scrollview.cpp:726
bool ReadMemBoxes(int target_page, bool skip_blanks, const char *box_data, bool continue_on_failure, GenericVector< TBOX > *boxes, GenericVector< STRING > *texts, GenericVector< STRING > *box_texts, GenericVector< int > *pages)
Definition: boxread.cpp:66
const char * string() const
Definition: strngs.cpp:198
int push_back(T object)
int NumPages() const
Definition: imagedata.h:227
const STRING & box_text(int index) const
Definition: imagedata.h:154
float x() const
Definition: points.h:209
Pix * PreScale(int target_height, int max_height, float *scale_factor, int *scaled_width, int *scaled_height, GenericVector< TBOX > *boxes) const
Definition: imagedata.cpp:227
void Text(int x, int y, const char *mystring)
Definition: scrollview.cpp:658
DocumentCache(inT64 max_memory)
Definition: imagedata.cpp:567
bool DeSerializeClasses(bool swap, FILE *fp)
bool Serialize(FILE *fp) const
const ImageData * GetPage(int index)
Definition: imagedata.cpp:448
void Display() const
Definition: imagedata.cpp:276
bool Serialize(FILE *fp) const
Definition: imagedata.cpp:83
Definition: strngs.h:45
int32_t inT32
Definition: host.h:38
const int kMaxReadAhead
Definition: imagedata.cpp:41
Definition: rect.h:30
bool Open(const STRING &filename, FileReader reader)
Definition: serialis.cpp:38
static void FromWordFeatures(const GenericVector< WordFeature > &word_features, GenericVector< FloatWordFeature > *float_features)
Definition: imagedata.cpp:99
void LoadPageInBackground(int index)
Definition: imagedata.cpp:436
#define ASSERT_HOST(x)
Definition: errcode.h:84
bool Serialize(TFile *fp) const
Definition: imagedata.cpp:165
int page_number() const
Definition: imagedata.h:130
char window_wait(ScrollView *win)
Definition: callcpp.cpp:111
void TextAttributes(const char *font, int pixel_size, bool bold, bool italic, bool underlined)
Definition: scrollview.cpp:641
void SetCursor(int x, int y)
Definition: scrollview.cpp:525
bool DeSerialize(bool swap, FILE *fp)
bool DeSerialize(bool swap, FILE *fp)
Definition: imagedata.cpp:91
void from_direction(uinT8 direction)
Definition: points.cpp:115
int FReadEndian(void *buffer, int size, int count)
Definition: serialis.cpp:97
bool DeSerialize(bool swap, FILE *fp)
Definition: strngs.cpp:163
void ReverseN(void *ptr, int num_bytes)
Definition: helpers.h:184
inT32 IntRand()
Definition: helpers.h:55
DocumentData(const STRING &name)
Definition: imagedata.cpp:376
static ImageData * Build(const char *name, int page_number, const char *lang, const char *imagedata, int imagedatasize, const char *truth_text, const char *box_text)
Definition: imagedata.cpp:134
bool CloseWrite(const STRING &filename, FileWriter writer)
Definition: serialis.cpp:140
Pix * GetPix() const
Definition: imagedata.cpp:217
int Modulo(int a, int b)
Definition: helpers.h:164
bool DeSerialize(TFile *fp)
Definition: imagedata.cpp:181
bool SaveDocument(const char *filename, FileWriter writer)
Definition: imagedata.cpp:410
const GenericVector< TBOX > & boxes() const
Definition: imagedata.h:148
friend void * ReCachePagesFunc(void *data)
Definition: imagedata.cpp:370
bool Serialize(FILE *fp) const
Definition: strngs.cpp:148
bool(* FileReader)(const STRING &filename, GenericVector< char > *data)
DocumentData * FindDocument(const STRING &document_name) const
Definition: imagedata.cpp:604
int MemoryUsed() const
Definition: imagedata.cpp:271
bool SerializeClasses(FILE *fp) const
int IntCastRounded(double x)
Definition: helpers.h:179
void Image(struct Pix *image, int x_pos, int y_pos)
Definition: scrollview.cpp:773
int FRead(void *buffer, int size, int count)
Definition: serialis.cpp:108
int64_t inT64
Definition: host.h:40
void AddPageToDocument(ImageData *page)
Definition: imagedata.cpp:428
void * ReCachePagesFunc(void *data)
Definition: imagedata.cpp:370
inT32 length() const
Definition: strngs.cpp:193
static void StartThread(void *(*func)(void *), void *arg)
Create new thread.
Definition: svutil.cpp:87
static void ComputeSize(const GenericVector< WordFeature > &features, int *max_x, int *max_y)
Definition: imagedata.cpp:55
void SetDocument(const char *filename, inT64 max_memory, FileReader reader)
Definition: imagedata.cpp:399