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