Skip to content

Extracting Text From a Scanned Fax With Header Noise on Every Page

10 min read · updated August 11, 2026

Every page of the recognised text starts with something like 02/14/2026 09:41 FROM: +1 555 0100 TO: ACCOUNTS PAYABLE P.003/012, sometimes garbled, sometimes running into the first real line. It is on all twelve pages, it is not part of the document, and it is poisoning your date extraction because it contains a date.

The strip you are trying to remove

The header band is worse than clutter for three specific reasons, and each is a bug you may already have.

  • It contains a plausible date. A field asking for “the date on the document” will happily return the transmission date, which is not the invoice date, the signature date or the service date. This is the single most common way fax header noise reaches a database.
  • It contains a plausible identifier. A phone number in the header is a numeric string in roughly the right shape to be mistaken for an account or reference number.
  • It merges into the first line. When the header sits close to the top of the content, recognition often emits both on one line, so a naive first-line-drop removes real content as well.

Where it comes from

The band is added by the sending machine, not by the document. In the Group 3 facsimile protocols standardised by the ITU-T, the sending terminal identifies itself in the protocol handshake, and it is conventional — and in some jurisdictions a regulatory requirement for commercial faxes — for the sender to print an identification line at the top of each transmitted page. That line is burned into the raster before transmission. By the time you have a TIFF or a PDF, the header is pixels, indistinguishable in kind from the document text.

Two consequences follow. There is no metadata field to read it from, so it must be recovered by recognition like everything else. And its content and position are properties of the sending machine, so they are consistent within one transmission and inconsistent across senders — which is precisely the property the detection method below exploits.

The pixels are not square

Before any of the header work, there is a step people skip that costs more accuracy than the header does. The Group 3 standards define asymmetric scanning resolutions: roughly 204 dots per inch horizontally, paired with about 98 lines per inch in standard mode and about 196 in fine mode. Standard-resolution fax pixels are therefore about twice as tall as they are wide.

An image decoded from such a file and handed straight to a recogniser that assumes square pixels sees characters vertically compressed by roughly a factor of two. Recognisers trained on ordinary scans handle that badly, and the failure looks like generalised poor quality rather than like an aspect-ratio problem, so it usually gets blamed on the fax being “low quality”.

The fix is to read the resolution tags the file records — a TIFF carries the horizontal and vertical resolution as separate tagged fields — and rescale to a square-pixel image before recognition. Do not hard-code the ratio: fine-mode pages are close to square already, and rescaling those by two makes them worse. The general handling of this stage is on the OCR pipeline page.

Treat the exact resolutions as what the ITU-T Group 3 recommendations specify, published by the International Telecommunication Union, rather than as a number to rely on from memory. What matters operationally is the ratio recorded in your own file, which you should read rather than assume.

Detecting furniture by repetition

The robust way to identify the header is not to describe it but to notice that it repeats. Recognise every page fully, keeping line text and line bounding boxes. Then normalise each line by replacing every digit with a placeholder and collapsing whitespace, and count how many pages contain each normalised line near the same y position.

import re
from collections import defaultdict

def normalise(line):
    return re.sub(r"\d", "#", line.strip().lower())

seen = defaultdict(set)
for page_no, lines in enumerate(pages):          # lines: (text, y_top)
    for text, y_top in lines:
        if y_top < 0.08 * page_height or y_top > 0.92 * page_height:
            seen[normalise(text)].add(page_no)

furniture = {k for k, pgs in seen.items() if len(pgs) >= 0.7 * len(pages)}

Digit normalisation is what makes this work. The header differs between pages only in the page counter and sometimes the minute, so once digits are masked the lines become identical and the count is high. Restricting the comparison to the top and bottom bands avoids matching a body line that legitimately recurs.

The threshold is a judgement. Requiring the line on every page is too strict: a page may fail recognition on the header, or the sending machine may not print it on the cover. Requiring it on a clear majority is enough, and a single-page fax has no repetition signal at all, which is a case to handle by falling back on position and pattern.

Where stripping belongs in the pipeline

Strip after recognition, not before, and strip lines rather than pixels. Cropping a fixed band off the top of every page image is the intuitive fix and it fails in both directions: on pages where the header is absent it eats the first line of content, and on pages where the sender’s machine prints lower than usual it leaves half the header behind. Working on recognised lines with bounding boxes lets you make the decision per page with evidence.

  1. Decode pages, read the resolution tags and rescale to square pixels.
  2. Deskew, then recognise, keeping per-line text and bounding boxes.
  3. Build the normalised-line index across all pages of the transmission and mark the furniture set.
  4. Remove furniture lines from the reading text, but keep them in a separate transmission_metadata block — they are the only record of the send time and the sending number.
  5. Run field extraction over the cleaned text, and validate any date it returns against the rules on date field validation.

What not to strip

The page counter in the header, usually printed as a page number over a total, is genuinely useful: it tells you how many pages the sending machine believed it was sending. Compare it with the number of pages you actually received and with the count claimed on the cover sheet, and a disagreement means a page was lost in transmission — which is worth knowing before you conclude that a signature is missing. That reconciliation is covered on extracting a fax cover sheet.

Similarly, do not strip a repeating line just because it repeats. A continuation-page header on the document itself — a policy number reprinted at the top of each page, a report title, a “Page 3 of 9” footer belonging to the original document rather than to the fax — will match the same repetition test. Distinguish them by content pattern before removing: transmission furniture carries a telephone number, a send time, or the sender identification format, and document furniture carries identifiers you probably want to extract.