Tools,

Tesseract psm modes: which one to use, tested

Use psm 3 for normal pages, psm 4 for tables, psm 6 for receipts, psm 7 for a cropped line or word, and psm 11 for scattered labels. The wrong mode gives no error, only less or scrambled text. On the same 72 invoices and stock lists, psm 6 read 137 of 2,418 numbers when the table had grid lines, and psm 3 put 410 in the right row when it had no lines. psm 4 read 2,349 and 2,359. On a word crop with a 20 pixel white border, psm 8 read 63 of 160 and psm 7 read 158.

Try it on your PDF, $0.006 a page

Which psm to use

Your image Use What I measured
A normal page, one column or more --psm 3, the default psm 3 and 1 alone kept two columns in order: 7,137 of 7,246 words, against about 3,700
A table, and you do not know if it has lines --psm 4 2,349 of 2,418 numbers with a grid, 2,359 with rules under the rows, 2,359 with no lines
A receipt --psm 6 336 of 381 rows, against 239 for psm 4 and 68 for psm 3
Scattered labels, a diagram, a form --psm 11 726 of 800 words, against 682 for psm 3
One cropped line, word or cell --psm 7, with 5 to 10 px of white around it 159 of 160 words
One cropped character --psm 7 or 10 The same text on all 124 crops, 101 right at best
A page that may be turned --psm 1 5,459 of 5,910 words at a quarter turn left, against 12 for psm 3

To set it:

tesseract scan.jpg out --psm 6
import pytesseract

text = pytesseract.image_to_string("scan.jpg", config="--psm 6")

All 14 modes, and what each one did

The names are from tesseract --help-psm in version 5.5.3, and you can pass a name in place of the number. --psm single_block gave the same output as --psm 6.

psm Name What it did in my tests
0 osd_only No text. Prints the page's turn and script.
1 auto_osd The same text as psm 3 on every upright page, for 9 to 51% more CPU. Also reads turned pages.
2 auto_only No text. The help says "not implemented", but it printed the orientation and a deskew angle.
3 auto The default. Best on normal pages, weak on tables with no lines and on receipts.
4 single_column Like psm 3 with no column finding. Best all-round on tables.
5 single_block_vert_text 227 of 14,693 words on upright book pages. It read pages turned a quarter right.
6 single_block Best on receipts and tables with no lines. Lost most or all rows of every grid table.
7 single_line Joint best on crops, with 6 and 10. On a full page it read 1 word of 14,693, and 41 of 55 pages came back blank.
8 single_word Fine on a tight crop. Falls apart as the border grows.
9 circle_word Did not read curved text. It only tries to remove a ring around a word.
10 single_char The same text as psm 7 on every crop, and it read whole lines. It does not limit you to one character.
11 sparse_text Best on scattered labels. Loses reading order and table rows.
12 sparse_text_osd Like psm 11 (725 of 800 label words), and it read turned book pages.
13 raw_line The same text as psm 8 on all my pages and crops. They parted only on the ring tests.

The results on full pages

I ran every mode on each set with Tesseract 5.5.3 and its default English model. The book pages are real: 55 printed pages from 11 books, 14,693 words checked by two people on Wikisource. The rest I made, so I know the true text: invoices, two column pages, receipts and label sheets, tilted, blurred and saved as JPEG like a scan.

Pages Scored on psm 3 psm 4 psm 6 psm 11
55 book pages Words in order, of 14,693 13,858 13,848 13,839 13,747
12 two column pages Words in order, of 7,246 7,137 3,728 3,767 3,721
72 invoices and stock lists, grid lines Numbers in the right row, of 2,418 2,337 2,349 137 37
Same, rough scans Numbers, of 2,418 2,079 2,090 121 40
Same, rules under the rows only Numbers, of 2,418 2,356 2,359 2,358 39
Same, no lines Numbers, of 2,418 410 2,359 2,367 42
24 receipts Item and price on one line, of 381 68 239 336 0
12 label sheets Words anywhere, of 800 682 636 637 726

On plain prose you can pick almost anything. The modes only start to differ once the layout does, and on tables the best read over 50 times as much as the worst.

psm 11's low table scores are about order, not reading. It found 1,984 of the 2,418 numbers somewhere on the page. It just does not keep them in rows. The two column pages show the same thing: psm 4 and 6 read 7,133 and 7,132 words in any order, but they ran straight across the gap, as psm 6 did here:

Not in this country? Now, then, what account do you door; but as you know, sir, it were locked on the
give of what's happened since he came back? inside, and all | could do, | rung up everyone, and

The full page modes are 3, 4, 6, 11 and their turned cousins 1 and 12. The rest are not for pages. On the 55 book pages, psm 5 read 227 words, psm 8 and 13 read 14, psm 10 read 6, psm 9 read 4, and psm 7 read 1.

Tables: the lines decide it

The same 72 invoices and stock lists, the same text and the same scan look. Only the lines changed.

Table psm 3 psm 4 psm 6
Grid lines 2,337 2,349 137
Rules under the rows 2,356 2,359 2,358
No lines 410 2,359 2,367

psm 6 on a grid. Here is one whole page. All 11 rows of the table are gone, and nothing tells you:

Northfield Supply - Invoice 1735
Date: October 12, 2026. Terms: net 30.

[SKU | Description [ty | Unit price | Amount |
Thank you for your business.

Modes 1 to 4, 11 and 12 go through a layout step, and that step calls FindAndRemoveLines in pagesegmain.cpp. psm 6 skips it, so the grid stays in the image. I did not trace the exact step that drops the text, but rules under the rows did no harm, so closed cells seem to be what it cannot take. Each of the 4 fonts read between 1 and 64 numbers of about 600.

psm 3 with no lines. It sees columns and reads them one at a time, so on most pages the numbers leave their rows:

SKU
1001
1002
1003
...
Description
Tile spacers

It read 2,071 numbers somewhere on the page, and 410 in the right row. psm 4 is psm 3 without the column search. pagesegmain.cpp says "no attempt is made to divide the image into columns", and column finding is on for modes 1 to 3 only (publictypes.h). That one change kept all three tables whole, and it is the mode I would try first on a table I have not seen.

psm 4 still gives you words, not cells. To rebuild rows and columns, see Tesseract table extraction.

Receipts: psm 6, and watch fixed width fonts

The gap between psm 4 and 6 is all Courier New. This is item and price read on one line, in 8 receipts of each font:

Font Rows psm 3 psm 4 psm 6
Courier New 123 0 0 98
Arial 120 41 112 111
Times 138 27 127 127

On Courier New, psm 3 and 4 printed the items as one list, with no price on any item line. psm 6 was the only mode that held them together. If your till prints fixed width, start with psm 6. It is not perfect: it got 345 of 381 prices, and one line read Lemons tos 32. Add the items up and check them against the total.

Crops: psm 7, and leave a border

The Tesseract docs say to crop "to a text area with reasonable border (e.g. 10 px)". The mode you choose decides how much that matters. This is 160 words and IDs like INV-32931 and $4,317.61, cut with 2 pixels of margin, then given a pure white border:

White border added psm 3 psm 7 psm 8
None 87 149 148
5 px 147 159 154
10 px 158 158 123
20 px 158 158 63
40 px 158 158 9

So crop with a little room, add white, and use psm 7:

import pytesseract
from PIL import Image, ImageOps

crop = Image.open("scan.jpg").convert("L").crop((200, 420, 1400, 500))
crop = ImageOps.expand(crop, border=10, fill=255)
text = pytesseract.image_to_string(crop, config="--psm 7")

fill=255 assumes dark text on a white page. For a number field, add a whitelist too: Tesseract digits only.

Single characters. I ran 124 of them, 62 in each of 2 fonts. psm 3 returned nothing at every border. psm 7 and 10 gave the same text every time, with 98 right at 5 pixels, 96 at 10 and 83 with no border added. The second sweep reached 101 at 10 pixels. Many misses were look-alikes and doubles: o as O, l as |, c as Cc. One letter gives the model little to go on, so read the whole word if you can.

Turned pages: psm 1, 5 and 12

I turned 22 book pages each way and read them. Words in order, of 5,910:

Page turned psm 1 psm 3 psm 5 psm 6 psm 12
A quarter turn right 5,552 5,552 5,542 94 5,499
A quarter turn left 5,459 12 15 87 5,393

So psm 3 copes with one direction and not the other, and psm 5 reads the same direction psm 3 does. psm 5 is meant for vertical scripts. I have only the English model, so that use is not tested. Book pages are easy, though. On tables and forms psm 1 did far worse, and turning the page yourself did better. That test is on Tesseract orientation detection.

psm 0, 1 and 12 need osd.traineddata beside your language file. Without it, psm 1 printed this, then read the page without any turn:

Failed loading language 'osd'
Tesseract couldn't load any languages!
Warning: Auto orientation and script detection requested, but osd language failed to load

psm 9 does not read around a circle

I hoped for a stamp reader. I drew 20 words bent around a ring, twice each, and every mode read 0 of them. In pagesegmain.cpp, psm 9 calls RemoveEnclosingCircle, so it tries to take a ring away from around a word. It does not unbend anything. With the same 40 words straight inside a drawn ring, psm 9 read 10, the same as psm 3, 6 and 7, and psm 8 read 0. For those, crop inside the ring: psm 3 and 7 then read all 40.

The flag, and the errors you will see

You pass You get
-psm 6 Error, unknown command line argument '-psm'
--psm=6 Error, unknown command line argument '--psm=6'
--psm 14 Invalid PSM value, please enter a symbolic PSM value or a number between 0-13
config="--psm 0" or 2 in pytesseract FileNotFoundError on a temp file, because these modes write no text

In pytesseract, the two bad spellings raise TesseractError with the same message. For psm 0, call the function made for it:

print(pytesseract.image_to_osd("scan.jpg"))

One trap has no error at all. A config setting worked for psm 4 and 11, so you would expect it to work for 6:

tesseract scan.jpg out -c tessedit_pageseg_mode=6

That gave psm 3 output, byte for byte. The library's own default is 6 and the program's is 3, so the program swaps a 6 for a 3. A comment in tesseract.cpp admits it: "the only way to force tesseract::PSM_SINGLE_BLOCK is from the command line." Use --psm 6.

psm and oem are different things. --psm is the layout, and --oem is the engine. --oem 1, --oem 3 and no flag gave the same output. --oem 0 and 2 ask for the old engine, which the default model leaves out, so they stop with Error: Tesseract (legacy) engine requested, but components are not present. The models are on tessdata_best vs tessdata_fast.

Do not let confidence pick the mode

The obvious shortcut is to run modes 3, 4, 6 and 11, then keep the one with the highest mean word confidence. I tried it, page by page:

Pages Picked by confidence Best single mode
Book pages 13,826 13,858 (psm 3)
Two column pages 4,856 7,137 (psm 3)
Invoices, grid lines 96 2,349 (psm 4)
Invoices, no lines 237 2,367 (psm 6)
Receipts 195 336 (psm 6)
Label sheets 726 726 (psm 11)

It chose psm 11 on 65 of the 72 grid pages, the mode that scatters the rows. Confidence rates the words, not the layout. More on what it does and does not catch: Tesseract confidence score. Instead, run the modes on a few pages and read the output:

import pytesseract

for psm in (3, 4, 6, 11):
    text = pytesseract.image_to_string("scan.jpg", config=f"--psm {psm}")
    print(f"--- psm {psm}\n{text}")

If you do not want to build it

PDF OCR to Markdown is our Apify Actor. It renders each scanned page at 300 dpi in gray, turns it upright, and reads it at psm 3. If that misreads columns it reads again at psm 4. Ruled tables are read cell by cell at psm 7 with a white border. You paste links to PDFs or images and get Markdown back, with tables as Markdown tables.

I did not run these tests on it, so no number above describes it.

What I did not test

Questions people ask

What is the default psm in Tesseract?

psm 3, fully automatic page segmentation. The tesseract program and pytesseract both use it when you pass no flag. The library's own default is psm 6, which is why -c tessedit_pageseg_mode=6 gave psm 3 output in my test.

What does psm 6 do in Tesseract?

It treats the image as one uniform block of text and skips the layout step. That suited receipts and tables with no lines, where it read 336 of 381 rows and 2,367 of 2,418 numbers. On a table with grid lines it read 137 of 2,418.

What is the difference between psm 6 and psm 11?

psm 6 keeps lines together, and psm 11 looks for any text in no particular order. On scattered labels, psm 11 read 726 of 800 words and psm 6 read 637. On receipts, psm 6 kept 336 of 381 rows together and psm 11 kept 0.

How do I set psm in pytesseract?

Pass it in config, as in image_to_string(image, config="--psm 6"). Use two dashes and a space: -psm 6 and --psm=6 both raise TesseractError. psm 0 and 2 write no text file, so image_to_string raises FileNotFoundError. Use image_to_osd for psm 0.

Which psm is best for a single line or word?

psm 7, with 5 to 10 pixels of white around the crop. It read 158 or 159 of 160 words at every white border from 5 to 40 pixels. psm 8 fell from 154 at 5 pixels to 63 at 20 and 9 at 40.

What is the difference between psm and oem in Tesseract?

psm sets the layout Tesseract expects, and oem sets the engine. With the default model, --oem 1, --oem 3 and no flag gave the same output. --oem 0 and 2 ask for the legacy engine, which that model does not include, so they stop with an error.

Also in this set: how to improve Tesseract accuracy, best DPI for OCR, Tesseract table extraction, Tesseract orientation detection, pytesseract PDF to text.

How this was made: Tack, our AI personality, ran these tests on October 31, 2026, and copied every result above from those runs. Apple M5 Max, Homebrew Tesseract 5.5.3 with its default English model (tessdata_fast 4.1.0), pytesseract 0.3.13, Pillow 13.0.0, Python 3.13, 8 workers, OMP_THREAD_LIMIT=1. CPU figures are one run each. Every setting but the psm stayed at its default. Modes 1 and 3 to 13 ran on every set, except that the turned pages ran modes 1, 3, 5, 6 and 12, and the border sweeps ran 5 or 6 modes. Modes 0 and 2 give no text. The book pages are the printed Wikisource pages of the tessdata page, read with --dpi 300, and a word counts if it matches exactly, in order. The grid invoices are the clean and rough 300 dpi sets of best DPI for OCR, scored by the row scorer of the confidence score page, which drops table rules from each word first. Here one read line counts for one row only. The rules and no lines sets are the clean invoices drawn again with only the border style changed. The two column pages use text from the same books, 449 to 620 words a page in 4 fonts. The receipts have 8 to 16 items plus subtotal, tax and total, and a row counts only if every word of the item and the exact price sit on one line. The label sheets hold 40 labels each at 9 to 26 point in 4 fonts. Those 3 sets have the rough scan look: a tilt of up to 1.2 degrees, blur, noise of 10 to 14 gray levels, and JPEG quality 70 to 80. The crops are drawn by Pillow at 46 pixel type, about 11 point at 300 dpi, in 4 fonts (2 for single characters), with blur 0.8, noise of 6 gray levels and JPEG quality 85, and saved with no dpi. A crop counts only if the output equals the text. The white border table adds pure white to crops that keep 2 pixels of their own margin. The 20 pixel samples and the no margin counts come from a second sweep whose margin carries the same noise as the text. Every Python and shell block ran as printed on copies of those files, apart from the output name. The scripts and their output are in our private repo. Sources, read October 31, 2026: Improving the quality of the output (tessdoc), and at Tesseract 5.5.3 tesseract.cpp, pagesegmain.cpp, linerec.cpp and publictypes.h. We make the Apify Actors. Tesseract, pytesseract, Pillow and Wikisource are free projects we do not make.