Skip to content

golibjpeg ​

ReleaseGoDoc

JPEG for Go — baseline and lossless JPEG, JPEG-LS, and JPEG XT decode; JPEG and JPEG-LS encode. No CGO for callers.

bash
go get github.com/godicom-dev/golibjpeg
FormatStandardDecodeEncode
JPEGISO 10918-1 (baseline / lossless)✅✅
JPEG-LSISO 14495 (lossless / near-lossless)✅✅
JPEG XTISO 18477 (HDR)✅—

Aligned with pylibjpeg-libjpeg's libjpeg.utils.

You may not need this directly

godicom already uses golibjpeg for JPEG and JPEG-LS Pixel Data. Reach for golibjpeg itself when you have a JPEG that is not inside a DICOM dataset, or want encoder parameters godicom does not expose.

Decode ​

stream may be a []byte, a file path (string), or an io.Reader.

go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/godicom-dev/golibjpeg"
)

func main() {
	data, err := os.ReadFile("image.jpg")
	if err != nil {
		log.Fatal(err)
	}

	img, err := golibjpeg.Decode(data) // format auto-detected
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%dx%d, %d components, precision %d\n",
		img.Width, img.Height, img.Components, img.Precision)

	_ = img.Pixels // RGB, or grayscale if the source is
}

Forcing a format, when the bytes are ambiguous or you already know:

go
img, err := golibjpeg.DecodeWithFormat(data, golibjpeg.FormatJPEGLS)

Or with a colour transform, matching the Python default of 0:

go
img, err := golibjpeg.DecodeImage(data, golibjpeg.ColourTransformNone)

ColourTransform values: ColourTransformNone (0), ColourTransformYCbCr (1), ColourTransformRCT (2), ColourTransformFreeform (3).

Parameters without pixels ​

go
params, err := golibjpeg.GetParameters(data)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("%dx%d, %d components, precision %d\n",
	params.Width, params.Height, params.Components, params.Precision)

Encode ​

Baseline JPEG, lossy — Quality is the usual 1–100 knob:

go
out, err := golibjpeg.Encode(pixels, golibjpeg.EncodeOptions{
	Columns: 512, Rows: 512, SamplesPerPixel: 3, BitsPerSample: 8,
	FrameType:       golibjpeg.FrameBaseline,
	ColourTransform: golibjpeg.ColourTransformYCbCr,
	Quality:         90,
})

JPEG-LS, lossless — no quality, and 16-bit samples are fine:

go
out, err := golibjpeg.Encode(frame, golibjpeg.EncodeOptions{
	Columns: 512, Rows: 512, SamplesPerPixel: 1, BitsPerSample: 16,
	FrameType:      golibjpeg.FrameJPEGLS,
	LSInterleaving: golibjpeg.LSInterleaveSample,
})

Input is interleaved little-endian pixels.

API ​

go
// Decode JPEG / JPEG-LS / JPEG XT
func DecodeImage(stream any, colourTransform ColourTransform) (*Image, error)

// Encode interleaved little-endian pixels to JPEG / JPEG-LS
func Encode(src []byte, opts EncodeOptions) ([]byte, error)

// DICOM encapsulated pixel data
func DecodePixelData(src []byte, opts PixelDataOptions) ([]byte, error)
func EncodePixelData(src []byte, desc PixelDataDescriptor, opts EncodePixelDataOptions) ([]byte, error)

// Parameters without decoding
func GetImageParameters(stream any) (*Params, error)

// Shorthands
func Decode(data []byte) (*Image, error)
func GetParameters(data []byte) (*Params, error)

How it works, and what that costs ​

Go wraps a C++ shared library through purego rather than CGO. The library is embedded per platform with //go:embed and extracted to a temp directory on first use. The C++ decode logic follows pylibjpeg-libjpeg (lib/interface/ plus thorfdbg/libjpeg).

Decoding is stripe-based — eight lines at a time — which keeps memory pressure down on large images. Output is native precision, 8- or 16-bit, planar-interleaved.

The cost is the same as for goopenjpeg: decoding needs a prebuilt library. Every desktop platform is covered:

OSamd64arm64
Linux✅✅
macOS✅✅
Windows✅✅

Anywhere else the module still builds, and every function returns an error wrapping ErrUnsupportedPlatform instead. Loading is lazy, so nothing panics at program start.

Repository layout ​

golibjpeg.go          # public API
native/               # purego loader + embedded prebuilt libs
lib/
  libjpeg/            # submodule → thorfdbg/libjpeg
  interface/          # decode + streamhook, from pylibjpeg-libjpeg
  capi/               # C ABI for purego
ref/pylibjpeg-libjpeg # read-only reference submodule
testdata/             # optional conformance JPEGs
bash
git clone --recurse-submodules https://github.com/godicom-dev/golibjpeg.git
cd golibjpeg
go test ./...

The libraries in native/libs/ are not built locally by default. CI builds them when lib/** changes on main and commits the result, which is what lets go get work with no CMake:

bash
gh workflow run build-libs.yml   # rebuild on CI without touching lib/
make build-native                # or build locally, requires CMake

The reference tests in reference_compliance_test.go mirror pylibjpeg-libjpeg's test_parameters.py and test_decode.py over its 23-image REF_JPG table. The images are fetched, not vendored:

bash
bash scripts/fetch-testdata.sh
go test ./...

See also ​

Released under the MIT License.