Skip to content

Fixed the GetUTF8Text method to return nullptr instead of asserting #4451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

zdenop
Copy link
Contributor

@zdenop zdenop commented Aug 10, 2025

Description

This PR fixes a crash in the GetUTF8Text method by returning nullptr instead of asserting when best_choice is nullptr.

Background

The issue was originally reported in sirfz/tesserocr#324, where a crash/assertion failure occurred when passing an empty image to tesserocr/Tesseract. The error message was:

best_choice != nullptr:Error:Assert failed: in file F:\Projects\Community\tesseract\src\ccmain\ltrresultiterator.cpp, line 52

While reproducing the issue with a test C++ program, I encountered a similar crash in another location:

it_->word()->best_choice != nullptr:Error:Assert failed: in file F:\Projects\Community\tesseract\src\ccmain\resultiterator.cpp, line 709

Changes

  • Modified GetUTF8Text to safely return nullptr if best_choice is nullptr, instead of triggering an assertion failure.

Additional Notes

  • I tested this with Warp2, which suggested updating multiple instances of ASSERT_HOST(best_choice != nullptr);. This PR addresses the crash in the context of GetUTF8Text.

  • There are still 3 other occurrences of this assertion in the following files:

    • src/ccmain/recogtraining.cpp
    • src/ccstruct/pageres.cpp

    It's unclear if those should also be replaced, as they may be used in different contexts (e.g., training or layout analysis). Further review is recommended before changing them.

Reproduction

Here is a minimal test program that reproduces the crash (when using an empty image):

//  test_ResultIterator.cpp
#include <leptonica/allheaders.h>
#include <tesseract/baseapi.h>

#include <iostream>

int main(int argc, char *argv[]) {
  tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();

  if (api->Init(NULL, "chi_sim")) {
    fprintf(stderr, "Could not initialize tesseract.\n");
    exit(1);
  }

  // Create a small empty/blank image (1x1 pixel)
  Pix* pix = pixCreate(100, 100, 8);
  if (pix == nullptr) {
    std::cerr << "Could not create empty image." << std::endl;
    return 1;
  }
  api->SetImage(pix);

  api->SetPageSegMode(tesseract::PSM_SINGLE_CHAR);
  api->Recognize(0);
  int out_offset;
  float out_slope;
  if (api->GetTextDirection(&out_offset, &out_slope)) {
    std::cout << "out_offset: " << out_offset << std::endl;
    std::cout << "out_slope: " << out_slope << std::endl;
  } else {
    std::cout << "GetTextDirection failed." << std::endl;
  }
  // Get the iterator and try to imitate relevant part of MapWordConfidences function
  tesseract::ResultIterator* ri = api->GetIterator();
  tesseract::PageIteratorLevel level = tesseract::RIL_WORD;
  if (ri != nullptr) {
    do {
      const char* word = ri->GetUTF8Text(level);
      auto word_confidence = ri->Confidence(level);
      std::cout << "Word confidence(" << word << "): " << word_confidence << std::endl;
    } while (ri->Next(level));
  }

  delete ri;
  api->End();
  delete api;
  pixDestroy(&pix);
  std::cout << "Test completed successfully - no crash!" << std::endl;
  return 0;
}

@@ -61,7 +63,9 @@ char *LTRResultIterator::GetUTF8Text(PageIteratorLevel level) const {
do { // for each text line in a paragraph
do { // for each word in a text line
best_choice = res_it.word()->best_choice;
ASSERT_HOST(best_choice != nullptr);
if (best_choice == nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an example which triggers the assertion here? I am not sure whether the break is the right solution here. Maybe the loop should continue at line 71.

@@ -104,37 +108,47 @@ float LTRResultIterator::Confidence(PageIteratorLevel level) const {
case RIL_BLOCK:
do {
best_choice = res_it.word()->best_choice;
mean_certainty += best_choice->certainty();
++certainty_count;
if (best_choice != nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there situations where the condition is false?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants