Dagger
Search

tesseract

recognition as a `dagger call` instead of the usual hand-rolled Dockerfile
plus rescue-the-output shell script. Hand it an image and get back plain
text, or hOCR / ALTO / TSV / PAGE / searchable PDF for anything that needs
word positions and confidences.

There is no official Tesseract container image — upstream ships source only
— so this module assembles its own the way the qemu module does: a
module-pinned Alpine plus `apk add tesseract-ocr`, with only the registry
prefix caller-overridable for air-gapped mirrors.

The language set lives on the root object rather than on Document because on
Alpine each language is a separate apk package (`tesseract-ocr-data-`,
none of which the base package pulls in). Selecting a language changes what
the image *is*, not just what a flag says; Document.WithLanguage only picks a
subset of what was installed. WithTessdata is the same decision for models
Alpine has no package for — a directory of `.traineddata` merged into the
image's datadir, and from there indistinguishable from a packaged language.

PDF input is rasterized rather than read, because leptonica cannot read PDF
at all. That work happens in its own container — Alpine at the same pinned
tag, plus poppler-utils and a font — instead of on the toolchain image,
which is a decision about who pays for it. Unconditionally installing both
packages takes the toolchain image from 67.1MiB to 81.4MiB, a 21% tax on
every caller who only ever hands this module a PNG. Rasterizing separately
leaves that image untouched and costs a PDF caller a 35.0MiB container of
which the 8.0MiB Alpine base is already shared, so the extra bytes are paid
once, by the callers who asked for them, and the rasterization caches
separately from recognition on top of that.

File map (all `package main`, surfaced as one Dagger module):

- enums.go — PageSegMode / EngineMode / Format enums plus the token and
output-extension tables that map them onto the CLI.
- options.go — the recognition option set Document and Batch share: one
builder, one piece of argv and one deferred check per option, so the two
units of work cannot drift apart.
- document.go — *Document, one image in and one artifact set out.
- batch.go — *Batch, a directory in and a mirrored directory out, all
of it in a single container exec.
- pdf.go — FromPdf, the rasterizer that turns a PDF into pages a
*Document can recognise.
- training.go — *Training, the other direction: images plus ground truth
in, a fine-tuned model out.

Installation

dagger install github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d

Entrypoint

Return Type
Tesseract !
Arguments
NameTypeDefault ValueDescription
registryString !"docker.io"Container registry hosting the alpine image.
alpineTagString !"3.24"Tag of the alpine image the toolchain is assembled on.
languages[String ! ] -Language codes to install, one apk package each. Empty installs "eng". "osd" is not a recognition language but is required by Document.Osd.
ompThreadLimitInteger -Upper bound on the OpenMP threads tesseract may use, set on the assembled image as OMP_THREAD_LIMIT. Unset, tesseract uses one thread per available CPU, which is what a caller who owns the machine wants. Set it when several recognitions share the cores — concurrent passes each claiming every CPU oversubscribe the box badly enough to cost an order of magnitude, which is the shape of the long-standing upstream slowdown reports (tesseract-ocr/tesseract#2611, #1171, #263). One thread per pass is the usual setting there.
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string
func (m *MyModule) Example(registry string, alpineTag string) *dagger.Tesseract  {
	return dag.
			Tesseract(registry, alpineTag)
}
@function
def example(registry: str, alpinetag: str, ) -> dagger.Tesseract:
	return (
		dag.tesseract(registry, alpinetag)
	)
@func()
example(registry: string, alpineTag: string, ): Tesseract {
	return dag
		.tesseract(registry, alpineTag)
}

Types

Tesseract 🔗

Tesseract is the root namespace for every exported function in this module. It carries the image coordinates and the language set the image is built with; Document hangs off it so the generated SDK surfaces recognition under dag.Tesseract().Document(...).

batch() 🔗

Batch binds a directory of images to the toolchain, for the shape a scanned-document pipeline actually arrives in: a folder of pages rather than one file at a time.

Export returns a directory mirroring the input layout, so a batch composes with whatever reads the results the same way the input directory was composed. Which files take part is a glob, defaulting to the image extensions leptonica can read.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
sourceDirectory !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string batch --source DIR_PATH
func (m *MyModule) Example(registry string, alpineTag string, source *dagger.Directory) *dagger.TesseractBatch  {
	return dag.
			Tesseract(registry, alpineTag).
			Batch(source)
}
@function
def example(registry: str, alpinetag: str, source: dagger.Directory) -> dagger.TesseractBatch:
	return (
		dag.tesseract(registry, alpinetag)
		.batch(source)
	)
@func()
example(registry: string, alpineTag: string, source: Directory): TesseractBatch {
	return dag
		.tesseract(registry, alpineTag)
		.batch(source)
}

container() 🔗

Container returns the assembled toolchain image. This is the escape hatch for everything this module does not wrap — the training binaries the apk package ships, combine_tessdata, and tesseract’s long tail of renderers stay reachable via container with-exec.

A requested OpenMP bound lives here rather than on the recognition invocation so everything reached through this escape hatch inherits it too.

Return Type
Container !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string container
func (m *MyModule) Example(registry string, alpineTag string) *dagger.Container  {
	return dag.
			Tesseract(registry, alpineTag).
			Container()
}
@function
def example(registry: str, alpinetag: str, ) -> dagger.Container:
	return (
		dag.tesseract(registry, alpinetag)
		.container()
	)
@func()
example(registry: string, alpineTag: string, ): Container {
	return dag
		.tesseract(registry, alpineTag)
		.container()
}

document() 🔗

Document binds one image to the toolchain.

The boundary input is a *dagger.File rather than a *dagger.Directory, unlike the kicad module’s Project: tesseract resolves nothing relative to its input, so one image — including a multi-page TIFF — is the whole unit of work.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
sourceFile !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string document --source file:path
func (m *MyModule) Example(registry string, alpineTag string, source *dagger.File) *dagger.TesseractDocument  {
	return dag.
			Tesseract(registry, alpineTag).
			Document(source)
}
@function
def example(registry: str, alpinetag: str, source: dagger.File) -> dagger.TesseractDocument:
	return (
		dag.tesseract(registry, alpinetag)
		.document(source)
	)
@func()
example(registry: string, alpineTag: string, source: File): TesseractDocument {
	return dag
		.tesseract(registry, alpineTag)
		.document(source)
}

fromPdf() 🔗

FromPdf binds a PDF to the toolchain by rasterizing its pages first, which is the one thing Document cannot do for itself: tesseract reads images through leptonica, and leptonica has no PDF support at all.

The result is an ordinary Document, so every output the module offers — Text, the per-format renderers, Export, and the recognition options — works on a PDF exactly as it does on an image. A multi-page PDF stays one document: the renderers accumulate pages, so Text returns the whole document with form feeds between pages and Pdf returns one searchable PDF with the same page count as the source.

dpi is the resolution the pages are rasterized at, and is declared to tesseract as the source resolution too, since it is exactly known here. Raising it costs time and memory roughly with its square; lowering it below 300 costs recognition accuracy on body text, because tesseract’s models were trained on characters of a certain pixel height.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
sourceFile !-

PDF whose pages are rasterized and then recognised.

dpiInteger !300

Resolution to rasterize each page at, in dots per inch.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string from-pdf --source file:path --dpi integer
func (m *MyModule) Example(registry string, alpineTag string, source *dagger.File, dpi int) *dagger.TesseractDocument  {
	return dag.
			Tesseract(registry, alpineTag).
			Frompdf(source, dpi)
}
@function
def example(registry: str, alpinetag: str, source: dagger.File, dpi: int) -> dagger.TesseractDocument:
	return (
		dag.tesseract(registry, alpinetag)
		.frompdf(source, dpi)
	)
@func()
example(registry: string, alpineTag: string, source: File, dpi: number): TesseractDocument {
	return dag
		.tesseract(registry, alpineTag)
		.fromPdf(source, dpi)
}

langs() 🔗

Langs returns the language codes the image can recognise in, as reported by tesseract --list-langs: the packaged languages and any model WithTessdata added, as one set. This is what Document.WithLanguage validates against, and it includes “osd” when that model was installed or supplied.

Return Type
[String ! ] !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string langs
func (m *MyModule) Example(ctx context.Context, registry string, alpineTag string) []string  {
	return dag.
			Tesseract(registry, alpineTag).
			Langs(ctx)
}
@function
async def example(registry: str, alpinetag: str, ) -> List[str]:
	return await (
		dag.tesseract(registry, alpinetag)
		.langs()
	)
@func()
async example(registry: string, alpineTag: string, ): Promise<string[]> {
	return dag
		.tesseract(registry, alpineTag)
		.langs()
}

parameters() 🔗

Parameters returns tesseract’s control-variable table — every name, its default value and a one-line description — as tesseract --print-parameters prints it. These are the names Document.WithParameter accepts.

Return Type
String !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string parameters
func (m *MyModule) Example(ctx context.Context, registry string, alpineTag string) string  {
	return dag.
			Tesseract(registry, alpineTag).
			Parameters(ctx)
}
@function
async def example(registry: str, alpinetag: str, ) -> str:
	return await (
		dag.tesseract(registry, alpinetag)
		.parameters()
	)
@func()
async example(registry: string, alpineTag: string, ): Promise<string> {
	return dag
		.tesseract(registry, alpineTag)
		.parameters()
}

training() 🔗

Training binds a directory of image and ground-truth pairs to the toolchain and fine-tunes a model against them, which is recognition run backwards: the text is what you have and the model is what you want.

It is here rather than behind Container because the apk package already ships every binary the job needs — lstmtraining, combine_tessdata, lstmeval — so what stands between a directory of transcribed lines and a .traineddata is orchestration rather than installation: a box file per image, a training sample per box, one training run, one freeze.

The model it produces pairs directly with WithTessdata, so a fine-tune and the recognition that uses it are two calls on the same module.

Return Type
Training !
Arguments
NameTypeDefault ValueDescription
sourceDirectory !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string training --source DIR_PATH
func (m *MyModule) Example(registry string, alpineTag string, source *dagger.Directory) *dagger.TesseractTraining  {
	return dag.
			Tesseract(registry, alpineTag).
			Training(source)
}
@function
def example(registry: str, alpinetag: str, source: dagger.Directory) -> dagger.TesseractTraining:
	return (
		dag.tesseract(registry, alpinetag)
		.training(source)
	)
@func()
example(registry: string, alpineTag: string, source: Directory): TesseractTraining {
	return dag
		.tesseract(registry, alpineTag)
		.training(source)
}

version() 🔗

Version returns the tesseract release the assembled image ships, as the bare version number reported by tesseract --version.

Return Type
String !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string version
func (m *MyModule) Example(ctx context.Context, registry string, alpineTag string) string  {
	return dag.
			Tesseract(registry, alpineTag).
			Version(ctx)
}
@function
async def example(registry: str, alpinetag: str, ) -> str:
	return await (
		dag.tesseract(registry, alpinetag)
		.version()
	)
@func()
async example(registry: string, alpineTag: string, ): Promise<string> {
	return dag
		.tesseract(registry, alpineTag)
		.version()
}

withTessdata() 🔗

WithTessdata adds a directory of .traineddata models to the image, which is the only way to reach a model Alpine has no package for: a fine-tuned model, a tessdata_best or tessdata_fast variant, or a language whose package simply does not exist.

Every file whose name ends in .traineddata becomes a language named after its stem — deu_frak.traineddata is the language deu_frak — so a model is renamed by renaming its file. Langs reports the union of these and the packaged ones, and everything that takes a language name accepts either.

The directory is merged with the packaged models rather than replacing them, because tesseract’s datadir is more than a bag of models: it also holds the renderer configfiles and the font the PDF renderer needs. A caller-supplied model wins over a packaged one of the same name, which is what makes replacing the stock eng with a fine-tuned one work.

Return Type
Tesseract !
Arguments
NameTypeDefault ValueDescription
dirDirectory !-

Directory of .traineddata models to make available to recognition.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 --registry string --alpine-tag string with-tessdata --dir DIR_PATH
func (m *MyModule) Example(registry string, alpineTag string, dir *dagger.Directory) *dagger.Tesseract  {
	return dag.
			Tesseract(registry, alpineTag).
			Withtessdata(dir)
}
@function
def example(registry: str, alpinetag: str, dir: dagger.Directory) -> dagger.Tesseract:
	return (
		dag.tesseract(registry, alpinetag)
		.withtessdata(dir)
	)
@func()
example(registry: string, alpineTag: string, dir: Directory): Tesseract {
	return dag
		.tesseract(registry, alpineTag)
		.withTessdata(dir)
}

Batch 🔗

Batch is a directory of images plus the recognition options that apply to all of them. It carries the same options type Document does, so the two cannot drift: every With* here forwards to the shared builder.

export() 🔗

Export recognises every matched image and returns a directory mirroring the input layout, with one artifact per requested format per image: scans/a.png becomes scans/a.txt alongside scans/a.pdf.

The whole batch is one container exec. tesseract’s own list-file mode — a text file of image paths as the FILE argument — is not what does that here, because it treats the list as one multi-page document: it renders a single concatenated artifact set (one .txt with form-feed page breaks, one multi-page PDF) and offers no way to get the per-image files this returns. So the exec loops over the manifest instead, which keeps the expensive part — a container exec, its mounts and its cache lookup, per page — collapsed to one, while each image still gets its own output base.

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
formats[Enum ! ] !-

Output formats to render for each image in the batch.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 export
func (m *MyModule) Example(source *dagger.Directory, formats []) *dagger.Directory  {
	return dag.
			Tesseract().
			Batch(source).
			Export(formats)
}
@function
def example(source: dagger.Directory, formats: List[]) -> dagger.Directory:
	return (
		dag.tesseract()
		.batch(source)
		.export(formats)
	)
@func()
example(source: Directory, formats: []): Directory {
	return dag
		.tesseract()
		.batch(source)
		.export(formats)
}

files() 🔗

Files lists the images the batch will recognise, as paths relative to the source directory root, in the order they are processed.

It answers the question a glob always raises — did that pattern pick up what I meant? — without paying for the recognition, and it fails on an empty match exactly as Export does.

Return Type
[String ! ] !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 files
func (m *MyModule) Example(ctx context.Context, source *dagger.Directory) []string  {
	return dag.
			Tesseract().
			Batch(source).
			Files(ctx)
}
@function
async def example(source: dagger.Directory) -> List[str]:
	return await (
		dag.tesseract()
		.batch(source)
		.files()
	)
@func()
async example(source: Directory): Promise<string[]> {
	return dag
		.tesseract()
		.batch(source)
		.files()
}

withDpi() 🔗

WithDpi declares the source resolution (--dpi) for every image in the batch. See Document.WithDpi.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
dpiInteger !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-dpi --dpi integer
func (m *MyModule) Example(source *dagger.Directory, dpi int) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withdpi(dpi)
}
@function
def example(source: dagger.Directory, dpi: int) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withdpi(dpi)
	)
@func()
example(source: Directory, dpi: number): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withDpi(dpi)
}

withEngine() 🔗

WithEngine selects the OCR engine (--oem) for every image in the batch. See Document.WithEngine.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
modeEnum !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-engine
func (m *MyModule) Example(source *dagger.Directory, mode ) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withengine(mode)
}
@function
def example(source: dagger.Directory, mode: ) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withengine(mode)
	)
@func()
example(source: Directory, mode: ): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withEngine(mode)
}

withGlob() 🔗

WithGlob replaces the set of files to recognise with everything matching one glob pattern, resolved against the directory root. ** crosses directory boundaries, so **/*.tif reaches nested scans and receipts/*.png stays in one folder.

Setting a pattern also takes over the extension filtering the default does. That is deliberate: a caller who names the pattern knows what is in the directory, and leptonica sniffs content rather than trusting extensions, so **/*.scan is a reasonable thing to ask for. PDFs stay rejected either way, because leptonica genuinely cannot read them.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
patternString !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-glob --pattern string
func (m *MyModule) Example(source *dagger.Directory, pattern string) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withglob(pattern)
}
@function
def example(source: dagger.Directory, pattern: str) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withglob(pattern)
	)
@func()
example(source: Directory, pattern: string): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withGlob(pattern)
}

withLanguage() 🔗

WithLanguage selects the recognition language (-l) for every image in the batch. See Document.WithLanguage.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
langString !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-language --lang string
func (m *MyModule) Example(source *dagger.Directory, lang string) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withlanguage(lang)
}
@function
def example(source: dagger.Directory, lang: str) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withlanguage(lang)
	)
@func()
example(source: Directory, lang: string): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withLanguage(lang)
}

withPageSegmentation() 🔗

WithPageSegmentation sets how much layout analysis precedes recognition (--psm) for every image in the batch. See Document.WithPageSegmentation.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
modeEnum !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-page-segmentation
func (m *MyModule) Example(source *dagger.Directory, mode ) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withpagesegmentation(mode)
}
@function
def example(source: dagger.Directory, mode: ) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withpagesegmentation(mode)
	)
@func()
example(source: Directory, mode: ): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withPageSegmentation(mode)
}

withParameter() 🔗

WithParameter sets one of tesseract’s control variables (-c name=value) for every image in the batch. See Document.WithParameter.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
nameString !-No description provided
valueString !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-parameter --name string --value string
func (m *MyModule) Example(source *dagger.Directory, name string, value string) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withparameter(name, value)
}
@function
def example(source: dagger.Directory, name: str, value: str) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withparameter(name, value)
	)
@func()
example(source: Directory, name: string, value: string): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withParameter(name, value)
}

withUserPatterns() 🔗

WithUserPatterns supplies a pattern list (--user-patterns) for every image in the batch. See Document.WithUserPatterns.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
patternsFile !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-user-patterns --patterns file:path
func (m *MyModule) Example(source *dagger.Directory, patterns *dagger.File) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withuserpatterns(patterns)
}
@function
def example(source: dagger.Directory, patterns: dagger.File) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withuserpatterns(patterns)
	)
@func()
example(source: Directory, patterns: File): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withUserPatterns(patterns)
}

withUserWords() 🔗

WithUserWords supplies a word list (--user-words) for every image in the batch. See Document.WithUserWords.

Return Type
Batch !
Arguments
NameTypeDefault ValueDescription
wordsFile !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 batch --source DIR_PATH \
 with-user-words --words file:path
func (m *MyModule) Example(source *dagger.Directory, words *dagger.File) *dagger.TesseractBatch  {
	return dag.
			Tesseract().
			Batch(source).
			Withuserwords(words)
}
@function
def example(source: dagger.Directory, words: dagger.File) -> dagger.TesseractBatch:
	return (
		dag.tesseract()
		.batch(source)
		.withuserwords(words)
	)
@func()
example(source: Directory, words: File): TesseractBatch {
	return dag
		.tesseract()
		.batch(source)
		.withUserWords(words)
}

Document 🔗

Document is one unit of recognition plus the options that apply to it. It is immutable: every With* returns a copy, so a configured Document can be branched into several outputs without the branches interfering.

The unit comes in two shapes, and a document holds whichever it was built as: a single image in Source, or the rasterized pages of a PDF in Pages. They are alternatives rather than layers — exactly one is ever set — and everything downstream of validate is written against the resolved FILE argument, so the outputs, the options and the error paths are shared rather than reimplemented per shape.

The options themselves live on the shared options type, which Batch carries too — the builders here are forwarders, so a new recognition option reaches both units of work at once instead of being implemented twice.

alto() 🔗

Alto returns ALTO XML, the layout schema libraries and archives ingest.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 alto
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Alto()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.alto()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.alto()
}

box() 🔗

Box returns the character-level box file: one row per recognised character, giving the character and the box it was found in.

It is the format tesseract’s own training tooling corrects by hand — read the boxes, fix the characters the model got wrong, feed them back — and the most direct way to see where recognition thinks each glyph is. Hocr and Tsv carry boxes too, but at the word level and wrapped in a document format.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 box
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Box()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.box()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.box()
}

export() 🔗

Export runs one recognition pass and returns a directory holding every requested format.

It exists alongside the single-artifact functions because tesseract accepts several renderers per invocation: asking for text, hOCR and PDF together is one pass over the image, not three. Each artifact is named result plus the renderer’s own extension.

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
formats[Enum ! ] !-

Output formats to render in the single recognition pass.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 export
func (m *MyModule) Example(source *dagger.File, dpi int, formats []) *dagger.Directory  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Export(formats)
}
@function
def example(source: dagger.File, dpi: int, formats: List[]) -> dagger.Directory:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.export(formats)
	)
@func()
example(source: File, dpi: number, formats: []): Directory {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.export(formats)
}

hocr() 🔗

Hocr returns hOCR: HTML in which every recognised word carries its bounding box and confidence, which is what layout-aware post-processing reads.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 hocr
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Hocr()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.hocr()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.hocr()
}

lstmTrain() 🔗

LstmTrain returns one LSTM training sample — a .lstmf — pairing this image with the line of text it renders.

It is the unit Training is built out of, exposed on its own for the pipeline that wants to build its samples somewhere else: generate them here, keep them, and hand the collection to lstmtraining on its own terms. Training is the shorter path when the whole job is fine-tuning a model.

The ground truth is an argument rather than a file beside the image because a Document is one image, not a directory: there is nowhere for a .gt.txt to sit. It has to be a single line, and the image has to be a single line of text, for the same reason Training says so — the sample claims the whole image renders exactly this text.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
groundTruthString !-

The single line of text this image renders.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 lstm-train --ground-truth string
func (m *MyModule) Example(source *dagger.File, dpi int, groundTruth string) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Lstmtrain(groundTruth)
}
@function
def example(source: dagger.File, dpi: int, groundtruth: str) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.lstmtrain(groundtruth)
	)
@func()
example(source: File, dpi: number, groundTruth: string): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.lstmTrain(groundTruth)
}

osd() 🔗

Osd detects the document’s orientation and script without recognising any text (--psm 0), reporting the rotation needed to make the page upright.

It builds its own invocation rather than reusing the document’s recognition options: orientation detection runs off the osd model alone, so the selected language, engine and page-segmentation mode have nothing to say about it. The osd model has to be in the image: either as the package New installs for it, or as an osd.traineddata WithTessdata supplied.

Return Type
String !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 osd
func (m *MyModule) Example(ctx context.Context, source *dagger.File, dpi int) string  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Osd(ctx)
}
@function
async def example(source: dagger.File, dpi: int) -> str:
	return await (
		dag.tesseract()
		.frompdf(source, dpi)
		.osd()
	)
@func()
async example(source: File, dpi: number): Promise<string> {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.osd()
}

page() 🔗

Page returns PAGE XML, the PRImA layout-analysis schema — the alternative to ALTO for tools built around that ecosystem.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 page
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Page()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.page()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.page()
}

pdf() 🔗

Pdf returns a searchable PDF: the source image with an invisible text layer positioned behind it, so the page looks untouched but selects and greps.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 pdf
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Pdf()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.pdf()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.pdf()
}

processedImages() 🔗

ProcessedImages returns the image tesseract actually recognised, which is not the one it was given: recognition runs on a binarized, deskewed derivative, and this is that derivative as a TIFF.

It answers the question a disappointing result always raises — is the model wrong, or did the page never survive thresholding? A scan that comes back as a field of black has failed before recognition started, and no amount of tuning --psm will fix it.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 processed-images
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Processedimages()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.processedimages()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.processedImages()
}

text() 🔗

Text recognises the document and returns the plain text directly, by asking tesseract to write to stdout instead of a file. This is the shortest path for the common case; Txt is the same content as a *dagger.File.

Return Type
String !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 text
func (m *MyModule) Example(ctx context.Context, source *dagger.File, dpi int) string  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Text(ctx)
}
@function
async def example(source: dagger.File, dpi: int) -> str:
	return await (
		dag.tesseract()
		.frompdf(source, dpi)
		.text()
	)
@func()
async example(source: File, dpi: number): Promise<string> {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.text()
}

tsv() 🔗

Tsv returns tab-separated recognition results: one row per layout element, descending from page to word, each with its box and confidence.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 tsv
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Tsv()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.tsv()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.tsv()
}

txt() 🔗

Txt recognises the document and returns the plain text as a file. It is the same content Text returns; take this when the next step wants a file rather than a string.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 txt
func (m *MyModule) Example(source *dagger.File, dpi int) *dagger.File  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Txt()
}
@function
def example(source: dagger.File, dpi: int) -> dagger.File:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.txt()
	)
@func()
example(source: File, dpi: number): File {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.txt()
}

withDpi() 🔗

WithDpi declares the source image’s resolution (--dpi), which tesseract otherwise guesses from the image metadata. Guessing wrong hurts recognition on images that carry no resolution at all. A non-positive value is rejected at output time.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
dpiInteger !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-dpi --dpi integer
func (m *MyModule) Example(source *dagger.File, dpi int, dpi1 int) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withdpi(dpi1)
}
@function
def example(source: dagger.File, dpi: int, dpi1: int) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withdpi(dpi1)
	)
@func()
example(source: File, dpi: number, dpi1: number): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withDpi(dpi1)
}

withEngine() 🔗

WithEngine selects the OCR engine (--oem). Unset, tesseract picks based on what the language data provides.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
modeEnum !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-engine
func (m *MyModule) Example(source *dagger.File, dpi int, mode ) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withengine(mode)
}
@function
def example(source: dagger.File, dpi: int, mode: ) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withengine(mode)
	)
@func()
example(source: File, dpi: number, mode: ): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withEngine(mode)
}

withLanguage() 🔗

WithLanguage selects the recognition language (-l). Several languages can be combined with +, as in “eng+deu”, in which case tesseract loads all of them for one pass.

The value picks from what the image carries — the packages New installed and any model WithTessdata supplied — and cannot itself add a language, since both of those are baked in when the image is assembled. An unknown value is rejected at output time with the available set listed. Unset, recognition runs in the first language New installed.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
langString !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-language --lang string
func (m *MyModule) Example(source *dagger.File, dpi int, lang string) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withlanguage(lang)
}
@function
def example(source: dagger.File, dpi: int, lang: str) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withlanguage(lang)
	)
@func()
example(source: File, dpi: number, lang: string): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withLanguage(lang)
}

withPageSegmentation() 🔗

WithPageSegmentation sets how much layout analysis precedes recognition (--psm). Unset, tesseract uses fully automatic segmentation without orientation detection.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
modeEnum !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-page-segmentation
func (m *MyModule) Example(source *dagger.File, dpi int, mode ) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withpagesegmentation(mode)
}
@function
def example(source: dagger.File, dpi: int, mode: ) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withpagesegmentation(mode)
	)
@func()
example(source: File, dpi: number, mode: ): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withPageSegmentation(mode)
}

withParameter() 🔗

WithParameter sets one of tesseract’s control variables (-c name=value); Parameters lists every name and its default.

It takes a name and a value separately rather than a map because Dagger functions cannot accept map parameters. An unknown name is rejected at output time: tesseract itself only warns and carries on, so a typo would otherwise silently do nothing.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
nameString !-No description provided
valueString !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-parameter --name string --value string
func (m *MyModule) Example(source *dagger.File, dpi int, name string, value string) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withparameter(name, value)
}
@function
def example(source: dagger.File, dpi: int, name: str, value: str) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withparameter(name, value)
	)
@func()
example(source: File, dpi: number, name: string, value: string): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withParameter(name, value)
}

withUserPatterns() 🔗

WithUserPatterns supplies a pattern list (--user-patterns): one pattern per line describing the shape of expected strings, such as part numbers.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
patternsFile !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-user-patterns --patterns file:path
func (m *MyModule) Example(source *dagger.File, dpi int, patterns *dagger.File) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withuserpatterns(patterns)
}
@function
def example(source: dagger.File, dpi: int, patterns: dagger.File) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withuserpatterns(patterns)
	)
@func()
example(source: File, dpi: number, patterns: File): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withUserPatterns(patterns)
}

withUserWords() 🔗

WithUserWords supplies a word list (--user-words): one word per line, which recognition then favours. Useful for jargon and proper nouns the packaged dictionary does not know.

Return Type
Document !
Arguments
NameTypeDefault ValueDescription
wordsFile !-No description provided
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 from-pdf --source file:path --dpi integer \
 with-user-words --words file:path
func (m *MyModule) Example(source *dagger.File, dpi int, words *dagger.File) *dagger.TesseractDocument  {
	return dag.
			Tesseract().
			Frompdf(source, dpi).
			Withuserwords(words)
}
@function
def example(source: dagger.File, dpi: int, words: dagger.File) -> dagger.TesseractDocument:
	return (
		dag.tesseract()
		.frompdf(source, dpi)
		.withuserwords(words)
	)
@func()
example(source: File, dpi: number, words: File): TesseractDocument {
	return dag
		.tesseract()
		.fromPdf(source, dpi)
		.withUserWords(words)
}

Training 🔗

Training is a directory of image plus ground-truth pairs bound to the toolchain, and the fine-tuning run that turns them into a model.

The unit of work is one text line: each image holds a single line and its .gt.txt holds the text that line renders, which is the shape tesseract’s own training data takes and the reason the ground truth is rejected when it carries more than one line. A page of text is not a training sample; it is as many samples as it has lines, and cutting it into them is a decision about the data rather than about this module.

Fine-tuning needs a float base model, which nothing Alpine packages is: every model in tesseract-ocr/tessdata is quantized to integers for recognition speed and lstmtraining refuses to continue from one. The float models live in tesseract-ocr/tessdata_best, and reach this module the same way any other unpackaged model does — through WithTessdata. That is why WithBaseModel is required rather than defaulting to the recognition language: the default would be a model that cannot be trained.

evaluate() 🔗

Evaluate runs lstmeval against the fine-tuned model and returns the error rates it reports: BCER, the character error rate, and BWER, the word error rate, both as percentages.

It evaluates against the training set, which makes this a measure of how well the model fit the data it was shown rather than of how it will do on data it has not seen. Those are different numbers and the second one is the one that matters for a model going into production: hold part of the ground truth back, and build a second Training over it to measure that.

The run is shared with Traineddata rather than repeated — both read the same finished container — so asking for the model and its error rate costs one training run, not two.

Return Type
String !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 training --source DIR_PATH \
 evaluate
func (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string  {
	return dag.
			Tesseract().
			Training(source).
			Evaluate(ctx)
}
@function
async def example(source: dagger.Directory) -> str:
	return await (
		dag.tesseract()
		.training(source)
		.evaluate()
	)
@func()
async example(source: Directory): Promise<string> {
	return dag
		.tesseract()
		.training(source)
		.evaluate()
}

files() 🔗

Files lists the images the run will train on, as paths relative to the source directory root, in the order they are presented.

It is where the pairing is checked, so it answers the question a training directory always raises — did every image find its ground truth? — without paying for the training run. The check is by name alone; what the ground truth files actually say is read when the run happens.

Return Type
[String ! ] !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 training --source DIR_PATH \
 files
func (m *MyModule) Example(ctx context.Context, source *dagger.Directory) []string  {
	return dag.
			Tesseract().
			Training(source).
			Files(ctx)
}
@function
async def example(source: dagger.Directory) -> List[str]:
	return await (
		dag.tesseract()
		.training(source)
		.files()
	)
@func()
async example(source: Directory): Promise<string[]> {
	return dag
		.tesseract()
		.training(source)
		.files()
}

traineddata() 🔗

Traineddata runs the fine-tuning and returns the resulting model, named after the base model it was trained from.

The file is a complete .traineddata: --stop_training folds the trained network back together with the base model’s unicharset and dictionaries, so it is a drop-in for the model it started from rather than a fragment needing assembly. Hand it to WithTessdata and it becomes a language like any other.

Return Type
File !
Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 training --source DIR_PATH \
 traineddata
func (m *MyModule) Example(source *dagger.Directory) *dagger.File  {
	return dag.
			Tesseract().
			Training(source).
			Traineddata()
}
@function
def example(source: dagger.Directory) -> dagger.File:
	return (
		dag.tesseract()
		.training(source)
		.traineddata()
	)
@func()
example(source: Directory): File {
	return dag
		.tesseract()
		.training(source)
		.traineddata()
}

withBaseModel() 🔗

WithBaseModel names the model fine-tuning starts from. It is required, and it has to be a float model: lstmtraining refuses to continue from a quantized one, and every model Alpine packages is quantized.

The float models are published as tesseract-ocr/tessdata_best, and are supplied to this module exactly as any other unpackaged model is — as a directory handed to WithTessdata. The name here is the one that directory gives the model, so a best.traineddata is the base model “best”.

The name is also what the trained model is called: fine-tuning “eng” produces an eng.traineddata, which is what makes the result drop straight back into WithTessdata as a replacement for the model it came from. Give it another name by putting it in a directory under one — WithTessdata reads the language off the file name.

Return Type
Training !
Arguments
NameTypeDefault ValueDescription
langString !-

Name of the model to fine-tune, as Langs reports it.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 training --source DIR_PATH \
 with-base-model --lang string
func (m *MyModule) Example(source *dagger.Directory, lang string) *dagger.TesseractTraining  {
	return dag.
			Tesseract().
			Training(source).
			Withbasemodel(lang)
}
@function
def example(source: dagger.Directory, lang: str) -> dagger.TesseractTraining:
	return (
		dag.tesseract()
		.training(source)
		.withbasemodel(lang)
	)
@func()
example(source: Directory, lang: string): TesseractTraining {
	return dag
		.tesseract()
		.training(source)
		.withBaseModel(lang)
}

withIterations() 🔗

WithIterations sets how many training iterations to run — one iteration is one training sample presented to the network, so a 40-line set runs 40 iterations per pass over the data.

The count is always bounded: lstmtraining left to itself trains until its error rate stops improving, which on a real data set is hours, so this module always passes --max_iterations and defaults it to 100. That default is deliberately far below what fine-tuning a model for production takes (upstream’s own worked example uses 400 for a single font, and thousands is ordinary) and is chosen instead to keep the first call a caller makes — and this module’s own test suite — finish in seconds rather than turning into an unattended job. Raise it for anything real.

Return Type
Training !
Arguments
NameTypeDefault ValueDescription
nInteger !-

Number of training iterations to run. Must be positive.

Example
dagger -m github.com/z5labs/devex/daggerverse/tesseract@c23370f6a9d38a8cc2e427fe427159135e7cc44d call \
 training --source DIR_PATH \
 with-iterations --n integer
func (m *MyModule) Example(source *dagger.Directory, n int) *dagger.TesseractTraining  {
	return dag.
			Tesseract().
			Training(source).
			Withiterations(n)
}
@function
def example(source: dagger.Directory, n: int) -> dagger.TesseractTraining:
	return (
		dag.tesseract()
		.training(source)
		.withiterations(n)
	)
@func()
example(source: Directory, n: number): TesseractTraining {
	return dag
		.tesseract()
		.training(source)
		.withIterations(n)
}