I just get only a small part of the table's content: how to make it work?

#47
by Raphy10-Collab - opened

Hi @staghado

I strangely do not get all the rows of a table

This is how I start LightOnOCR-2-1B :

./llama-server --cors-origins localhost -m LightOnOCR-2-1B.f16.gguf --mmproj LightOnOCR-2-1B-mmproj-f16.gguf

And this is the code I use to grab the OCR output:

       bool TextExtractionFromImg::getContent()
        {
            try
            {
                std::string imagefilepath_s(imagefilepath.begin(), imagefilepath.end());
                const auto image = read_binary_file(imagefilepath_s);
                const std::string encoded_image = base64_encode(image);
                const std::string mime_type = get_mime_type(imagefilepath_s);
    
                // "Output only the recognized text in Markdown.";
    
                const std::string prompt =
                    "OCR the document. Preserve the natural reading order, "
                    "tables, line breaks, rows, and mathematical notation. "
                    "output all the content of all rows till the end of the table";
    
                std::ostringstream json;
    
                json << R"({
                  "messages": [
                    {
                      "role": "user",
                      "content": [
                        {
                          "type": "image_url",
                          "image_url": {
                            "url": "data:)"
                             << mime_type
                             << ";base64,"
                             << encoded_image
                             << R"("
                          }
                        },
                        {
                          "type": "text",
                          "text": ")";
                        // This prompt contains no JSON-special characters.
                        json << prompt;
                        json << R"("
                        }
                      ]
                    }
                  ],
                  "temperature": 0.2,
                  "max_tokens": 1000
                })";
    
                const std::string request_body = json.str();
    
                CURL* curl = curl_easy_init();
    
                if (!curl)
                {
                    throw std::runtime_error("Could not initialize libcurl");
                    return false;
                }
                std::string response;
                struct curl_slist* headers = nullptr;
                headers = curl_slist_append(headers, "Content-Type: application/json");
    
                curl_easy_setopt(
                    curl,
                    CURLOPT_URL,
                    "http://127.0.0.1:8080/v1/chat/completions"
                );
    
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
                curl_easy_setopt(curl, CURLOPT_POST, 1L);
    
                curl_easy_setopt(
                    curl,
                    CURLOPT_POSTFIELDS,
                    request_body.c_str()
                );
    
                curl_easy_setopt(
                    curl,
                    CURLOPT_POSTFIELDSIZE,
                    static_cast<long>(request_body.size())
                );
                curl_easy_setopt(
                    curl,
                    CURLOPT_WRITEFUNCTION,
                    write_callback
                );
                curl_easy_setopt(
                    curl,
                    CURLOPT_WRITEDATA,
                    &response
                );
                curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
                curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600L);
    
                const CURLcode result = curl_easy_perform(curl);
                if (result != CURLE_OK)
                {
                    std::cerr << "Request failed: " << curl_easy_strerror(result) << '\n';
                else
                {
                    long status_code = 0;
                    curl_easy_getinfo(
                        curl,
                        CURLINFO_RESPONSE_CODE,
                        &status_code
                    );
    
                    if (status_code >= 200 && status_code < 300)
                    {
                        try
                        {
                            nlohmann::json obj = nlohmann::json::parse(response);
                            std::string html_s = obj["choices"][0]["message"]["content"];
    
                            std::cout << "html_s:" << std::endl;
                            std::cout << html_s << std::endl;
                            html_c = html_s.c_str();
    
                            std::cout << std::endl;
        
                            //getContentInMd();
    
                        }
                        catch (const nlohmann::json::parse_error& e)
                        {
                            std::cerr << "Invalid JSON: " << e.what() << '\n';
                        }
                    }
                    else
                    {
                        std::cerr << "HTTP " << status_code << ":\n" << response << '\n';
                    }
                }
                curl_slist_free_all(headers);
                curl_easy_cleanup(curl);
                return result == CURLE_OK ? true : false;
            }
            catch (const std::exception& error)
            {
                std::cerr << error.what() << '\n';
                return false;
            }
        }
            }

This is the image :
textImf-14

And this is the output:

html_s:
JUL 2026

# WORLD ECONOMIC OUTLOOK UPDATE: ANNEX

## Annex Table 1. Selected Economies: Real GDP Growth  
*(Percent change)*

<table>
  <thead>
    <tr>
      <th rowspan="2">Country</th>
      <th rowspan="2">2024</th>
      <th rowspan="2">2025</th>
      <th colspan="2">Projections</th>
      <th colspan="2">Difference from April 2026<br>WEO Projections 1/</th>
    </tr>
    <tr>
      <th>2026</th>
      <th>2027</th>
      <th>2026</th>
      <th>2027</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>Argentina</td><td>–1.3</td><td>4.4</td><td>3.5</td><td>4.0</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Australia</td><td>1.0</td><td>2.0</td><td>1.9</td><td>1.7</td><td>–0.1</td><td>0.0</td></tr>
    <tr><td>Brazil</td><td>3.4</td><td>2.3</td><td>2.4</td><td>2.2</td><td>0.5</td><td>0.2</td></tr>
    <tr><td>Canada</td><td>2.0</td><td>1.9</td><td>1.1</td><td>1.7</td><td>–0.4</td><td>–0.2</td></tr>
    <tr><td>China</td><td>5.0</td><td>5.0</td><td>4.6</td><td>4.1</td><td>0.2</td><td>0.1</td></tr>
    <tr><td>Egypt 2/</td><td>2.4</td><td>4.4</td><td>4.6</td><td>4.4</td><td>0.4</td><td>–0.4</td></tr>
    <tr><td>France</td><td>1.4</td><td>0.9</td><td>0.6</td><td>0.9</td><td>–0.3</td><td>0.0</td></tr>
    <tr><td>Germany</td><td>–0.5</td><td>0.2</td><td>0.7</td><td>1.0</td><td>–0.1</td><td>–0.2</td></tr>
    <tr><td>India 2/</td><td>7.1</td><td>7.7</td><td>6.4</td><td>6.7</td><td>–0.1</td><td>0.2</td></tr>
    <tr><td>Indonesia</td><td>5.0</td><td>5.1</td><td>5.0</td><td>5.1</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Iran 2/ 3/</td><td>3.7</td><td>–0.8</td><td>–5.4</td><td>2.9</td><td>0.7</td><td>–0.3</td></tr>
    <tr><td>Italy</td><td>0.8</td><td>0.5</td><td>0.5</td><td>0.5</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Japan</td><td>–0.2</td><td>1.1</td><td>0.6</td><td>0.7</td><td>–0.1</td><td>0.1</td></tr>

As you can see, the output is partial: it contains only a small part of the actual table data

Modifying the prompt in this way:

            const std::string prompt =
                "Transcribe the entire table visible in the image."
                "Requirements:"
                "- Include every row and every column."
                "- Preserve the original reading order."
                "- Do not omit headers, footnotes, totals, blank cells, or partially visible cells."
                "- Do not summarize or explain the table."
                "- Return only one complete HTML <table>."
                "- Use <thead> for the header when a header exists."
                "- Use <tbody> for data rows."
                "- Use colspan or rowspan when the image clearly contains merged cells."
                "- If a cell is unreadable, write [illegible] rather than inventing text.";

I, still, get the same very partial result:

# WORLD ECONOMIC OUTLOOK UPDATE: ANNEX

## JUL 2026

### Annex Table 1. Selected Economies: Real GDP Growth

#### (Percent change)

<table>
  <thead>
    <tr>
      <th rowspan="2">Country</th>
      <th rowspan="2">2024</th>
      <th rowspan="2">2025</th>
      <th colspan="2">Projections</th>
      <th colspan="2">Difference from April 2026<br>WEO Projections 1/</th>
    </tr>
    <tr>
      <th>2026</th>
      <th>2027</th>
      <th>2026</th>
      <th>2027</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>Argentina</td><td>-1.3</td><td>4.4</td><td>3.5</td><td>4.0</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Australia</td><td>1.0</td><td>2.0</td><td>1.9</td><td>1.7</td><td>-0.1</td><td>0.0</td></tr>
    <tr><td>Brazil</td><td>3.4</td><td>2.3</td><td>2.4</td><td>2.2</td><td>0.5</td><td>0.2</td></tr>
    <tr><td>Canada</td><td>2.0</td><td>1.9</td><td>1.1</td><td>1.7</td><td>-0.4</td><td>-0.2</td></tr>
    <tr><td>China</td><td>5.0</td><td>5.0</td><td>4.6</td><td>4.1</td><td>0.2</td><td>0.1</td></tr>
    <tr><td>Egypt 2/</td><td>2.4</td><td>4.4</td><td>4.6</td><td>4.4</td><td>0.4</td><td>-0.4</td></tr>
    <tr><td>France</td><td>1.4</td><td>0.9</td><td>0.6</td><td>0.9</td><td>-0.3</td><td>0.0</td></tr>
    <tr><td>Germany</td><td>-0.5</td><td>0.2</td><td>0.7</td><td>1.0</td><td>-0.1</td><td>-0.2</td></tr>
    <tr><td>India 2/</td><td>7.1</td><td>7.7</td><td>6.4</td><td>6.7</td><td>-0.1</td><td>0.2</td></tr>
    <tr><td>Indonesia</td><td>5.0</td><td>5.1</td><td>5.0</td><td>5.1</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Iran 2/ 3/</td><td>3.7</td><td>-0.8</td><td>-5.4</td><td>2.9</td><td>0.7</td><td>-0.3</td></tr>
    <tr><td>Italy</td><td>0.8</td><td>0.5</td><td>0.5</td><td>0.5</td><td>0.0</td><td>0.0</td></tr>
    <tr><td>Japan</td><td>-0.2</td><td>1.1</td><td>0.6</td><td>0.7</td><td>-0.1</td><td>0.1</td></tr>
    <tr><td>Kazakhstan</td><td>5.0

What am I doing wrong and/or missing?
How to get the whole table's content?

Hi, I think the issue is that you are asking for only 1000 tokens while for such tables you need 4k+.
One more thing, this model requires NO prompt and adding one hurts performance.

This comment has been hidden
LightOn AI org

you still have ")" as the prompt and try asking for 4096 tokens so you actually can inspect the output before it times out.

I removed the prompt, and set an higher max_tokens .

I get : "Timeout was reached" :

    bool TextExtractionFromImg::getContent()
    {
        try
        {
            std::string imagefilepath_s(imagefilepath.begin(), imagefilepath.end());
            const auto image = read_binary_file(imagefilepath_s);
            const std::string encoded_image = base64_encode(image);
            const std::string mime_type = get_mime_type(imagefilepath_s);

            std::ostringstream json;

            json << R"({
              "messages": [
                {
                  "role": "user",
                  "content": [
                    {
                      "type": "image_url",
                      "image_url": {
                        "url": "data:)"
                         << mime_type
                         << ";base64,"
                         << encoded_image
                         << R"("
                      }
                    },
                    {
                      "type": "text",
                      "text": ")";
                    // This prompt contains no JSON-special characters.
                    //json << prompt;
                    json << R"("
                    }
                  ]
                }
              ],
              "temperature": 0.2,
              "max_tokens": 5000
            })";

            const std::string request_body = json.str();

            CURL* curl = curl_easy_init();

            if (!curl)
            {
                throw std::runtime_error("Could not initialize libcurl");
                return false;
            }
            std::string response;
            struct curl_slist* headers = nullptr;
            headers = curl_slist_append(headers, "Content-Type: application/json");

            curl_easy_setopt(
                curl,
                CURLOPT_URL,
                "http://127.0.0.1:8080/v1/chat/completions"
            );

            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
            curl_easy_setopt(curl, CURLOPT_POST, 1L);

            curl_easy_setopt(
                curl,
                CURLOPT_POSTFIELDS,
                request_body.c_str()
            );

            curl_easy_setopt(
                curl,
                CURLOPT_POSTFIELDSIZE,
                static_cast<long>(request_body.size())
            );
            curl_easy_setopt(
                curl,
                CURLOPT_WRITEFUNCTION,
                write_callback
            );
            curl_easy_setopt(
                curl,
                CURLOPT_WRITEDATA,
                &response
            );
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 600L);
            curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600L);
            const CURLcode result = curl_easy_perform(curl);
            if (result != CURLE_OK)
            {
                std::cerr << "Request failed: " << curl_easy_strerror(result) << '\n';
            }
            else
            {
                long status_code = 0;
                curl_easy_getinfo(
                    curl,
                    CURLINFO_RESPONSE_CODE,
                    &status_code
                );

                if (status_code >= 200 && status_code < 300)
                {
                    try
                    {
                        nlohmann::json obj = nlohmann::json::parse(response);
                        std::string html_s = obj["choices"][0]["message"]["content"];

                        std::cout << "html_s:" << std::endl;
                        std::cout << html_s << std::endl;
                        html_c = html_s.c_str();

                        std::cout << std::endl;

                        //getMd(html_s);

                        //getContentInMd();

                    }
                    catch (const nlohmann::json::parse_error& e)
                    {
                        std::cerr << "Invalid JSON: " << e.what() << '\n';
                    }
                }
                else
                {
                    std::cerr << "HTTP " << status_code << ":\n" << response << '\n';
                }
            }
            curl_slist_free_all(headers);
            curl_easy_cleanup(curl);
            return result == CURLE_OK ? true : false;
        }
        catch (const std::exception& error)
        {
            std::cerr << error.what() << '\n';
            return false;
        }
    }
0.00.006.028 I cmn  common_param: common_params_print_info: verbosity = 3 (adjust with the `-lv N` CLI arg)
0.00.010.249 I srv    load_model: loading model 'LightOnOCR-2-1B.f16.gguf'
0.00.490.176 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden
0.05.512.988 I srv    load_model: loaded multimodal model, 'LightOnOCR-2-1B-mmproj-f16.gguf'
0.05.739.029 I srv    load_model: initializing, n_slots = 4, n_ctx_slot = 16384, kv_unified = 'true'
0.05.748.121 I srv  llama_server: model loaded
0.05.748.136 I srv  llama_server: listening on http://127.0.0.1:8080
raphy@raphy:~/LightOnOCR-2-1B-Test$ ./builddir/LightOnOCR-2-1B-Test 
Request failed: Timeout was reached


Interesting...   

If I set max_token to 2000, I get the whole table's data:
Country 2024 2025 Projections Difference from April 2026
WEO Projections 1/
2026 2027 2026 2027
Argentina–1.34.43.54.00.00.0
Australia1.02.01.91.7–0.10.0
Brazil3.42.32.42.20.50.2
Canada2.01.91.11.7–0.4–0.2
China5.05.04.64.10.20.1
Egypt 2/2.44.44.64.40.4–0.4
France1.40.90.60.9–0.30.0
Germany–0.50.20.71.0–0.1–0.2
India 2/7.17.76.46.7–0.10.2
Indonesia5.05.15.05.10.00.0
Iran 2/ 3/3.7–0.8–5.42.90.7–0.3
Italy0.80.50.50.50.00.0
Japan–0.21.10.60.7–0.10.1
Kazakhstan5.06.54.64.40.00.0
Korea2.21.12.62.50.70.4
Malaysia5.25.24.74.30.00.0
Mexico1.50.51.21.9–0.4–0.3
The Netherlands1.11.81.01.3–0.2–0.1
Nigeria4.14.04.14.30.00.0
Pakistan 2/2.63.23.63.50.00.0
Philippines5.74.43.95.5–0.2–0.3
Poland3.23.73.42.40.10.0
Russia4.91.01.11.10.00.0
Saudi Arabia2.64.61.75.5–1.41.0
South Africa0.51.11.11.30.10.0
Spain3.52.82.11.80.00.0
Thailand2.92.41.92.20.40.1
Türkiye3.33.62.93.6–0.50.1
United Kingdom1.01.41.01.30.20.0
United States2.8Screenshot from 2026-08-31 19-13-25

But if I set max_tokens to a bit higher: 2050
I get : "Timeout was reached" :

Sign up or log in to comment