cimg

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2023 License: BSD-3-Clause Imports: 11 Imported by: 0

README

C++ image library bindings for Go

cimg is a Go wrapper for various C/C++ image libraries, including:

  • libjpeg-turbo
  • stb_image_resize
  • Unrotate image so that natural encoding orientation is same as display orientation
  • Reading and writing EXIF orientation (provided via native Go code)

Why?

There are a host of high-performance C/C++ libraries out there for image manipulation, and it's near impossible to write the same kind of code in Go.

Example: Compress/Decompress with TurboJPEG
import "github.com/bearki/cimg"

func compressImage(width, height int, rgb []byte) {
	raw := cimg.Image{
		Width: width,
		Height: height,
		Stride: width * 3,
		Pixels: rgb,
	}
	params := cimg.MakeCompressParams(cimg.PixelFormatRGB, cimg.Sampling420, 35, 0)
	jpg, err := cimg.Compress(&raw, params)
}

func decompressImage(jpg []byte) (*Image, error) {
	return cimg.Decompress(jpg)
}
Example: Read and Modify EXIF Orientation
import "github.com/bearki/cimg"

func inspectOrientation(jpgRaw []byte) {
	// Parse JPEG/JFIF segments, and read EXIF Orientation tag
	jpgExif, err = cimg.LoadExif(jpgRaw)
	fmt.Printf("Orientation: %v\n", jpgExif.GetOrientation())

	// Modify EXIF rotation.
	// If the file contains no EXIF data, then this will create an
	// EXIF "segment".
	err = jpgExif.SetOrientation(3)
	out := bytes.Buffer{}
	err = jpgExif.Save(&out)
}
Example: Resize with stb_image_resize
import "github.com/bearki/cimg"

// Resize from bytes
func resizeImage(srcWidth, srcHeight int, rgba []byte, dstWidth, dstHeight int) *cimg.Image {
	src := cimg.WrapImage(srcWidth, srcHeight, 4, rgba)
	return cimg.ResizeNew(src, dstWidth, dstHeight)
}
C/C++ compiler optimizations

I was initially worried that I needed to add the directive #cgo CXXFLAGS: -O2, but it looks like cgo compiles with optimizations on by default. You can verify this by adding #cgo CXXFLAGS: -O0 to resize.go, and run go test -bench=.. Compare with -O0 and -O2 and there should be a big difference. Removing the comment entirely should give similar performance to -O2.

System requirements

I have only tested this on Ubuntu 20.04 amd64.

To install the necessary packages:

apt install libturbojpeg0-dev
Testing

Warning! Many of the Go unit tests don't actually validate their results. Instead, they simply write out a JPEG file into the test directory. It's your job to visually see that they look correct.

Documentation

Index

Constants

View Source
const (
	ExifTagOrientation = 0x112 // Photo orientation
)

Variables

View Source
var ErrNoAlpha = errors.New("Image has no alpha channel")

Functions

func Compress

func Compress(img *Image, params CompressParams) ([]byte, error)

Compress compresses an image using TurboJPEG

func NChan

func NChan(pf PixelFormat) int

NChan returns the number of channels of the pixel format

func Resize

func Resize(src, dst *Image) error

Resize resizes an image into a destination buffer that you provide Assumes sRGB image

func Transform

func Transform(jpegBytes []byte, x, y, w, h int, flags Flags) ([]byte, error)

JPEG image CROP using TurboJPEG

Types

type CompressParams

type CompressParams struct {
	Sampling Sampling
	Quality  int // 1 .. 100
	Flags    Flags
}

CompressParams are the TurboJPEG compression parameters

func MakeCompressParams

func MakeCompressParams(sampling Sampling, quality int, flags Flags) CompressParams

MakeCompressParams returns a fully populated CompressParams struct

type ExifData

type ExifData struct {
	// contains filtered or unexported fields
}

ExifData is a wrapper around github.com/dsoprea/go-exif, with only the tags exposed that I've needed to manipulate

func LoadExif

func LoadExif(jpeg []byte) (*ExifData, error)

Load a JPEG file, and parse it into it's JFIF segments. You can then read the existing EXIF data, or alter it. Note that if you modify the EXIF data, then reading that same data back from this data structure will not reflect your changes. You changes will only be reflected if you call Save(), and then reload that file using LoadJpegExif again.

func (*ExifData) GetOrientation

func (x *ExifData) GetOrientation() int

See https://www.impulseadventure.com/photo/exif-orientation.html 0th Row 0th Column 1 top left side 2 top right side 3 bottom right side 4 bottom left side 5 left side top 6 right side top 7 right side bottom 8 left side bottom

func (*ExifData) Save

func (x *ExifData) Save(w io.Writer) error

Save writes the image file

func (*ExifData) SetOrientation

func (x *ExifData) SetOrientation(orient int) error

Set photo orientation (See GetOrientation for meaning of the codes)

type Flags

type Flags C.int
const (
	FlagAccurateDCT   Flags = C.TJFLAG_ACCURATEDCT
	FlagBottomUp      Flags = C.TJFLAG_BOTTOMUP
	FlagFastDCT       Flags = C.TJFLAG_FASTDCT
	FlagFastUpsample  Flags = C.TJFLAG_FASTUPSAMPLE
	FlagNoRealloc     Flags = C.TJFLAG_NOREALLOC
	FlagProgressive   Flags = C.TJFLAG_PROGRESSIVE
	FlagStopOnWarning Flags = C.TJFLAG_STOPONWARNING
)

type Image

type Image struct {
	Pixels        []byte
	Width         int
	Height        int
	Stride        int
	Format        PixelFormat
	Premultiplied bool
}

Image is the concrete image type that is used by all functions inside cimg

func Decompress

func Decompress(encoded []byte, outFormat PixelFormat) (*Image, error)

Load an image into memory. JPEG: Uses TurboJPEG PNG: Uses Go's native PNG library TIFF: Uses golang.org/x/image/tiff The resulting image is RGB for JPEGs, or RGBA/Gray for PNG

func FromImage

func FromImage(src image.Image, allowDeepClone bool) (*Image, error)

Convert a Go image.Image into a cimg.Image If allowDeepClone is true, and the source image is type GRAY, NRGBA, or RGBA, then the resulting Image points directly to the pixel buffer of the source image.

func NewImage

func NewImage(width, height int, format PixelFormat) *Image

NewImage creates a new 8-bit image

func ResizeNew

func ResizeNew(src *Image, dstWidth, dstHeight int) *Image

ResizeNew allocates the output image for you and returns it Assumes sRGB image

func UnrotateExif

func UnrotateExif(exifOrientation int, src *Image) (*Image, error)

UnrotateExif rewrites the bytes of an image so that the EXIF orientation information can be discarded. In other words, after running UnrotateExif, the encoded image orientation is the same as the natural display image orientation. exifOrientation must be either 3, 6, or 8.

func WrapImage

func WrapImage(width, height int, format PixelFormat, pixels []byte) *Image

Wrap an array of bytes into an Image object (do not copy pixels)

func WrapImageStrided

func WrapImageStrided(width, height int, format PixelFormat, pixels []byte, stride int) *Image

Wrap an array of bytes into an Image object, with controllable stride (do not copy pixels)

func (*Image) AvgColor

func (img *Image) AvgColor() []uint8

AvgColor computes the average color of the entire image, per channel The averaging is performed in sRGB space (i.e. not linear light) If the image has more than 8 channels, then the function will panic

func (*Image) ChannelSpliter

func (img *Image) ChannelSpliter() []byte

ChannelSpliter 图像通道分离

func (*Image) Clone

func (img *Image) Clone() *Image

Clone returns a deep clone of the image

func (*Image) CopyImage

func (dst *Image) CopyImage(src *Image, dstX1, dstY1 int) error

CopyImage copies src into dst at the location dstX1, dstY1

func (*Image) CopyImageRect

func (dst *Image) CopyImageRect(src *Image, srcX1, srcY1, srcX2, srcY2 int, dstX1, dstY1 int) error

CopyImageRect copies src into dst, at dstX1,dstY1. The source imagery is read from the rectangle specified by the 4 source location parameters. All coordinates are clipped prior to drawing. The only error condition is when the two images have a different number of channels. Note that you will get swapped RGB channels if you do something like copy from an RGB image into a BGR image (i.e. this function does not swizzle the channels, it just does a dumb memcpy of the rows).

func (*Image) Matte

func (img *Image) Matte(r, g, b uint8)

For an RGBA image, blend it on top of the given color, so that transparent regions of the image will be filled with the given color. If the image has no alpha channel, then this is a no-op.

func (*Image) NChan

func (img *Image) NChan() int

NChan returns the number of channels of the pixel format of the image

func (*Image) Premultiply

func (img *Image) Premultiply()

Premultiply RGB by A. If the image does not have an alpha channel, or if Premultiplied=true then this is a no-op.

func (*Image) ResizeNew

func (img *Image) ResizeNew(w, h int) *Image

ResizeNew 缩放图像

func (*Image) ToImage

func (img *Image) ToImage() (image.Image, error)

ToImage returns an image from the Go standard library 'image' package

func (*Image) ToRGB

func (img *Image) ToRGB() *Image

ToRGB returns a 3 channel image. This is used to remove the alpha channel from an image that was loaded from a PNG, or to turn a gray image into an RGB image. If the image is already a 3 channel image, then a clone is returned

type PixelFormat

type PixelFormat C.int
const (
	PixelFormatRGB     PixelFormat = C.TJPF_RGB
	PixelFormatBGR     PixelFormat = C.TJPF_BGR
	PixelFormatRGBX    PixelFormat = C.TJPF_RGBX
	PixelFormatBGRX    PixelFormat = C.TJPF_BGRX
	PixelFormatXBGR    PixelFormat = C.TJPF_XBGR
	PixelFormatXRGB    PixelFormat = C.TJPF_XRGB
	PixelFormatGRAY    PixelFormat = C.TJPF_GRAY
	PixelFormatRGBA    PixelFormat = C.TJPF_RGBA
	PixelFormatBGRA    PixelFormat = C.TJPF_BGRA
	PixelFormatABGR    PixelFormat = C.TJPF_ABGR
	PixelFormatARGB    PixelFormat = C.TJPF_ARGB
	PixelFormatCMYK    PixelFormat = C.TJPF_CMYK
	PixelFormatUNKNOWN PixelFormat = C.TJPF_UNKNOWN
)

type Sampling

type Sampling C.int
const (
	Sampling444  Sampling = C.TJSAMP_444
	Sampling422  Sampling = C.TJSAMP_422
	Sampling420  Sampling = C.TJSAMP_420
	SamplingGray Sampling = C.TJSAMP_GRAY
	Sampling440  Sampling = C.TJSAMP_440
	Sampling411  Sampling = C.TJSAMP_411
)

func (Sampling) GetMCUHeight

func (v Sampling) GetMCUHeight() int

*

  • MCU block height (in pixels) for a given level of chrominance subsampling.
  • MCU block sizes:
  • - 8x8 for no subsampling or grayscale
  • - 16x8 for 4:2:2
  • - 8x16 for 4:4:0
  • - 16x16 for 4:2:0
  • - 32x8 for 4:1:1

func (Sampling) GetMCUWidth

func (v Sampling) GetMCUWidth() int

*

  • MCU block width (in pixels) for a given level of chrominance subsampling.
  • MCU block sizes:
  • - 8x8 for no subsampling or grayscale
  • - 16x8 for 4:2:2
  • - 8x16 for 4:4:0
  • - 16x16 for 4:2:0
  • - 32x8 for 4:1:1

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL