gojpegturbo

package module
v0.0.0-...-789bed6 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2024 License: Apache-2.0 Imports: 10 Imported by: 0

README

Build codecov License rcard

gojpegturbo

一个go封装 libjpeg-turbo 的库。 和其他封装不同的是,封装了特别多的接口,方便某些场景能够达到极致的性能,以最大程度利用libjpeg-turbo的特性。

Getting Start

局部图片解码

JPEG图片是由一个个MCU组成的,从左到右,从上到下排列。我们在哈夫曼编码解码后就能知道接下来的多少字节是一个MCU了。在某种特殊场景,需要剪裁其中一小块 区域,这时候局部解码就很有用,我们只需要找到对应区域所在的MCU并解码,能大大减少IDCT的次数,当然内存也减少很多。一个demo:

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/picone/gojpegturbo"
)

func main() {
	buf, err := ioutil.ReadFile("./testdata/test.jpg")
	if err != nil {
		log.Fatalln(err)
	}
	options := gojpegturbo.NewDecodeOptions()
	options.CropRect = &image.Rectangle{
		Min: image.Point{X: 100, Y: 200},
		Max: image.Point{X: 300, Y: 400},
	}
	outImg, err := gojpegturbo.Decode(buf, options)
	fmt.Println(outImg.Bounds().Max) // (200,200)
}
图片解码时缩放

图片解码时,如常用的4:2:2采样Cb和Cr通道是会进行升采样的,如果图片解码后边长需要等比缩放到比原来的1/2还小,可以使CbCr不进行生采样,Y通道使用降采样, 内存节省1/4,CPU也会使用更少。 如图片原来是600×800,如业务需要缩放至300×300,则可以使用options.ScaleNum = 1,options.ScaleDenom = 2 ,解码出来的图片将会是300×400,业务再使用别的缩放算法进行图片变换。jpeg解码过程中的图片缩放性能消耗极少,并且节省更多内存。完整demo如下:

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/picone/gojpegturbo"
)

func main() {
	buf, err := ioutil.ReadFile("./testdata/test.jpg")
	if err != nil {
		log.Fatalln(err)
	}
	options := gojpegturbo.NewDecodeOptions()
	options.ScaleNum = 1
	options.ScaleDenom = 2
	outImg, err := gojpegturbo.Decode(buf, options)
	fmt.Println(outImg.Bounds().Max) // (300,400)
}
更高级的解码参数

通过调整解码的参数,在接受图片质量稍微变差的同时能提供更快的速度,部分场景下适用(如生成较小的缩略图,图片质量并不那么重要了)。

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/picone/gojpegturbo"
)

func main() {
	buf, err := ioutil.ReadFile("./testdata/test.jpg")
	if err != nil {
		log.Fatalln(err)
	}
	options := gojpegturbo.NewDecodeOptions()
	options.DctMethod = gojpegturbo.DctMethodIntFast
	options.DitherMode = gojpegturbo.DitherOrdered
	options.TwoPassQuantize = false
	options.DoFancyUpSampling = false
	options.DesiredNumberOfColors = 216
	outImg, err := gojpegturbo.Decode(buf, options)
}

Performance

Decode性能测试

以下仅使用单张图片对比。

goos: darwin
goarch: amd64
pkg: github.com/picone/gojpegturbo
cpu: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz
BenchmarkDecodeC
BenchmarkDecodeC-4                    	     469	   2585729 ns/op	  33.79 MB/s
BenchmarkDecodeDctFast
BenchmarkDecodeDctFast-4              	     465	   2500954 ns/op	  34.93 MB/s
BenchmarkDecodeDoSloppierSampling
BenchmarkDecodeDoSloppierSampling-4   	     484	   2428223 ns/op	  35.98 MB/s
BenchmarkDecodeDitherOrdered
BenchmarkDecodeDitherOrdered-4        	     448	   3118717 ns/op	  28.01 MB/s
BenchmarkDecodeLessColors
BenchmarkDecodeLessColors-4           	     460	   2749298 ns/op	  31.78 MB/s
BenchmarkDecodeGo
BenchmarkDecodeGo-4                   	     135	   8480772 ns/op	  10.30 MB/s
PASS
Encode性能测试

以下为单张图片测试

goos: darwin
goarch: amd64
pkg: github.com/picone/gojpegturbo
cpu: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz
BenchmarkEncodeC
BenchmarkEncodeC-4                    	     508	   2334368 ns/op	  37.42 MB/s
BenchmarkEncodeFast
BenchmarkEncodeFast-4                 	     516	   2317246 ns/op	  37.70 MB/s
BenchmarkEncodeProgress
BenchmarkEncodeProgress-4             	      86	  12894303 ns/op	   6.78 MB/s
BenchmarkEncodeGo
BenchmarkEncodeGo-4                   	      43	  26813204 ns/op	   3.26 MB/s
PASS
缩放性能测试

由于使用了自己的ImageAttr类,图片缩放并不能很好地兼容nfnt/resize, 因此自己实现了 NearestNeighbor 和 Area 的图片缩放算法。

goos: darwin
goarch: amd64
pkg: github.com/picone/gojpegturbo
cpu: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz
BenchmarkImageAttr_ResizeArea
BenchmarkImageAttr_ResizeArea-4       	     310	   4008882 ns/op
BenchmarkImageAttr_ResizeNN
BenchmarkImageAttr_ResizeNN-4         	    2302	    478747 ns/op
BenchmarkImageAttr_ResizeBilinear
BenchmarkImageAttr_ResizeBilinear-4   	      48	  25154275 ns/op
PASS

Contributing

  • Please create an issue in issue list.
  • Contact Committers/Owners for further discussion if needed.
  • Following the golang coding standards.

License

gojpegturbo is under the Apache 2.0 license. See the LICENSE file for details.

Documentation

Overview

Package gojpegturbo 封装libjpeg-turbo库,优化图片的缩放和剪裁。

简单的图片解码编码:

img, err := gojpegturbo.Decode(buf, nil)
// modify img
buf, err = gojpegturbo.Encode(img, nil)

图片缩放:

options := gojpegturbo.NewDecodeOptions()
options.ExpectWidth = 50
options.ExpectHeight = 50
img, err := gojpegturbo.Decode(buf, options)
// 注意,这里输出的 img 的长和宽总是大于或等于 expect 的尺寸,会在图片解码阶段尽量逼近需要,但一般不会刚好等于,需要使用 resize 函数继续缩小。
img = img.ResizeNN(50, 50)
buf, err = gojpegturbo.Encode(img, nil)

图片剪裁:

options := gojpegturbo.NewDecodeOptions()
options.CropRect = &image.Rectangle{
	Min: image.Point{X: 100, Y: 200},
	Max: image.Point{X: 300, Y: 621},
}
// 这里输出的图片是已经剪裁好的。相比起其他图片解码库,优势在于解码的时候只解码感兴趣的 MCU 区域,大大节省 CPU 和内存。
img, err := gojpegturbo.Decode(buf, options)
buf, err = gojpegturbo.Encode(img, nil)

Index

Examples

Constants

View Source
const (
	// ColorSpaceUnknown 未知
	ColorSpaceUnknown = C.JCS_UNKNOWN
	// ColorSpaceGrayScale 灰色
	ColorSpaceGrayScale = C.JCS_GRAYSCALE
	// ColorSpaceRGB RGB
	ColorSpaceRGB = C.JCS_RGB
	// ColorSpaceYCbCr YCbCr
	ColorSpaceYCbCr = C.JCS_YCbCr
	// ColorSpaceCMYK CMYK
	ColorSpaceCMYK = C.JCS_CMYK
	// ColorSpaceYCCK YCCK
	ColorSpaceYCCK = C.JCS_YCCK
	// ColorSpaceExtRGB ExtRGB
	ColorSpaceExtRGB = C.JCS_EXT_RGB
	// ColorSpaceExtRGBX ExtRGBX
	ColorSpaceExtRGBX = C.JCS_EXT_RGBX
	// ColorSpaceExtBGR ExtBGR
	ColorSpaceExtBGR = C.JCS_EXT_BGR
	// ColorSpaceExtBGRX ExtBGRX
	ColorSpaceExtBGRX = C.JCS_EXT_BGRX
	// ColorSpaceExtXBGR ExtXBGR
	ColorSpaceExtXBGR = C.JCS_EXT_XBGR
	// ColorSpaceExtXRGB = ExtXRGB
	ColorSpaceExtXRGB = C.JCS_EXT_XRGB
	// ColorSpaceExtRGBA ExtRGBA
	ColorSpaceExtRGBA = C.JCS_EXT_RGBA
	// ColorSpaceExtBGRA ExtBGRA
	ColorSpaceExtBGRA = C.JCS_EXT_BGRA
	// ColorSpaceExtABGR ExtABGR
	ColorSpaceExtABGR = C.JCS_EXT_ABGR
	// ColorSpaceExtARGB ARGB
	ColorSpaceExtARGB = C.JCS_EXT_ARGB
	// ColorSpaceExtRGB565 RGB565
	ColorSpaceExtRGB565 = C.JCS_RGB565
)
View Source
const (
	// DctMethodIntSlow 使用int,较慢(精确)的DCT
	DctMethodIntSlow = C.JDCT_ISLOW
	// DctMethodIntFast 使用int且较快(不太精确)的DCT
	DctMethodIntFast = C.JDCT_IFAST
	// DctMethodFloat 使用浮点数的DCT
	DctMethodFloat = C.JDCT_FLOAT
)
View Source
const (
	// TjFlagFastDCT 使用更快速的DCT/IDCT算法
	TjFlagFastDCT = C.TJFLAG_FASTDCT
	// TjFlagAccurateDCT 使用更准确的DCT/IDCT算法
	TjFlagAccurateDCT = C.TJFLAG_ACCURATEDCT
	// TjFlagProgressive 使用渐进式编码
	TjFlagProgressive = C.TJFLAG_PROGRESSIVE
)

Variables

View Source
var (
	// ErrEmptyImage 传入图片的空的
	ErrEmptyImage = errors.New("empty image")
	// ErrEmptyDecode 解码结果是空的
	ErrEmptyDecode = errors.New("decode image empty")
	// ErrOptionsUnsupported 当前的选项并不支持
	ErrOptionsUnsupported = errors.New("options now unsupported")
)
View Source
var (
	// ErrImgEmpty 图片为空
	ErrImgEmpty = errors.New("image is empty")
	// ErrImgSizeInvalid 图片尺寸不合法
	ErrImgSizeInvalid = errors.New("image size is invalid")
	// ErrQualityOption 图片质量不合法
	ErrQualityOption = errors.New("quality option invalid")
)
View Source
var (
	// ErrWrongDstSize 输入的目标尺寸又唔
	ErrWrongDstSize = errors.New("error input dst image width or height")
)

Functions

func Encode

func Encode(img *ImageAttr, options *EncodeOptions) ([]byte, error)

Encode jpeg图片编码

Types

type ColorSpace

type ColorSpace C.J_COLOR_SPACE

ColorSpace 色彩空间

type DctMethod

type DctMethod C.J_DCT_METHOD

DctMethod DCT/IDCT的方法

type DecodeOptions

type DecodeOptions struct {
	// CropRect 图片剪裁区域,默认不剪裁
	CropRect *image.Rectangle
	// DctMethod 解码的时候使用的方法。
	// 现在的计算机上有AVX2,JDCT_IFAST和JDCT_ISLOW有相似的性能。如果JPEG图像使用85质量一下的等级压缩的,那么这两种算法
	// 应该没有差别。如果高于85,实践中如quality=97,JDCT_IFAST通常会比JDCT_ISLOW的PSNR低大约4-6dB。因此一般不是用
	// JDCT_IFAST。对于JDCT_FLOAT并不一定质量就好,因为每个机器的四舍五入情况不一致。
	DctMethod DctMethod
	// TwoPassQuantize 默认是true。
	TwoPassQuantize bool
	// DitherMode 抖动方法,默认是DitherFs。
	DitherMode DitherMode
	// DesiredNumberOfColors 颜色表使用的颜色数量,默认是256。
	DesiredNumberOfColors int
	// DoFancyUpSampling 升采样是否使用精确的,默认是true。
	DoFancyUpSampling bool
	// ScaleNum 按比例缩放图片,在MCU升降采样的时候就能生效,一般为1
	ScaleNum uint
	// ScaleDenom 缩放比例的分母,为了保证图片效果,一般取值为 1/2, 1/4, 1/8...
	ScaleDenom uint
	// ExpectWidth 预期宽度,根据图片宽高使用ScaleNum和ScaleDenom参数调整缩放比例。如果设置了会覆盖ScaleNum和ScaleDenom的值。这里缩放的
	// 意义在于能在解码阶段低成本的缩放图片,尽量节省 CPU 和内存,且对实际使用效果影响很小。
	//
	// WARNING: 这里只是期望值,并不是实际值,内部会尽量等比缩放到不低于期望值的最合理值,它始终只会以 1/2,1/4,1/8 这样的比例缩小图片。如输入
	// 尺寸为 600*800,期望值为 100*200,则实际结果为 150*200;若期望值为 250*300, 则实际结果为 300*400。
	ExpectWidth uint
	// ExpectHeight 预期高度,根据图片宽高使用ScaleNum和ScaleDenom参数调整缩放比例
	ExpectHeight uint
}

DecodeOptions 解码图片时的选项

func NewDecodeOptions

func NewDecodeOptions() *DecodeOptions

NewDecodeOptions 创建一个默认的解码图片选项

type DitherMode

type DitherMode C.J_DITHER_MODE

DitherMode 颜色抖动方法

const (
	// DitherNone 无,快,低质量。
	DitherNone DitherMode = C.JDITHER_NONE
	// DitherOrdered 有序抖动,中等速度,中等质量。
	DitherOrdered DitherMode = C.JDITHER_ORDERED
	// DitherFs Floyd-Steinberg算法的抖动,慢,高质量。
	DitherFs DitherMode = C.JDITHER_FS
)

type EncodeOptions

type EncodeOptions struct {
	// Quality 图片压缩质量
	Quality int
	// FastDct 快速的DCT
	FastDct bool
	// 精确的DCT
	AccurateDCT bool
	// SubSample 采样率
	SubSample TJSubSample
	// Progressive 是否使用渐进式编码
	Progressive bool
}

EncodeOptions 图片编码的选项

func NewEncodeOptions

func NewEncodeOptions() *EncodeOptions

NewEncodeOptions 创建一个默认的图片编码选项

type ImageAttr

type ImageAttr struct {
	Img                       []byte
	ImageWidth, ImageHeight   int        // 输出的图片宽高,若没有剪裁,和Origin的宽高一样
	OriginWidth, OriginHeight int        // 原始图片宽高
	ColorSpace                ColorSpace // 色彩空间。目前只有gray和YCbCr。
	ComponentsNum             int        // 颜色分量数,如YCbCr就是3。
}

ImageAttr 图像属性。保存图片原始宽高,目前宽高,颜色空间等基本属性。除此之外,实现了image.Image接口,能够直接使用众多第三方包二次处理。

func Decode

func Decode(img []byte, options *DecodeOptions) (*ImageAttr, error)

Decode 解码JPEG图片

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/picone/gojpegturbo"
)

func main() {
	buf, err := ioutil.ReadFile("./testdata/test.jpg")
	if err != nil {
		log.Fatalln(err)
	}
	options := gojpegturbo.NewDecodeOptions()
	options.DctMethod = gojpegturbo.DctMethodIntFast
	options.DitherMode = gojpegturbo.DitherOrdered
	options.TwoPassQuantize = false
	options.DoFancyUpSampling = false
	options.DesiredNumberOfColors = 216
	img, err := gojpegturbo.Decode(buf, options) // options can be nil and all options set to default.
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("width=%d,height=%d\n", img.Bounds().Max.X, img.Bounds().Max.Y)
}
Output:

width=600,height=800

func DecodeReader

func DecodeReader(r io.Reader, options *DecodeOptions) (*ImageAttr, error)

DecodeReader 解码reader过来的图片

func ResizeArea

func ResizeArea(src *ImageAttr, dstWidth, dstHeight int) (*ImageAttr, error)

ResizeArea 参考opencv的INTER_AREA算法,目前只能用于图片缩小,放大场景下效果不佳。

Example
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/picone/gojpegturbo"
)

func main() {
	fp, err := os.Open("./testdata/test.jpg")
	if err != nil {
		log.Fatalln(err)
	}
	srcImg, err := gojpegturbo.DecodeReader(fp, nil) // here you can cut your image with options.
	if err != nil {
		log.Fatalln(err)
	}
	// if you use Area algo, it SHOULD NOT pass width or height max than origin.
	dstImg, err := srcImg.ResizeArea(200, 400)
	if err != nil {
		log.Fatalln(err)
	}
	encOptions := gojpegturbo.NewEncodeOptions()
	encOptions.Quality = 60
	buf, err := gojpegturbo.Encode(dstImg, encOptions)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%v", len(buf) > 0) // here you can write `buf` to file
}
Output:

true

func ResizeNN

func ResizeNN(src *ImageAttr, dstWidth, dstHeight int) *ImageAttr

ResizeNN 邻近插值法缩放图片

func (*ImageAttr) At

func (img *ImageAttr) At(x, y int) color.Color

At 获取指定像素点的RBG颜色

func (*ImageAttr) Bounds

func (img *ImageAttr) Bounds() image.Rectangle

Bounds 选择范围。若还需要图片剪裁可以直接在这里返回别的值。目前只返回原始尺寸

func (*ImageAttr) ColorModel

func (img *ImageAttr) ColorModel() color.Model

ColorModel 色彩空间

func (*ImageAttr) PixelFormat

func (img *ImageAttr) PixelFormat() TJPixelFormat

PixelFormat 像素格式

func (*ImageAttr) ResizeArea

func (img *ImageAttr) ResizeArea(dstWidth, dstHeight int) (*ImageAttr, error)

ResizeArea 用 INTER_AREA 方法缩小图片,这个方法不能用于图片放大。

func (*ImageAttr) ResizeBilinear

func (img *ImageAttr) ResizeBilinear(dstWidth, dstHeight uint) image.Image

ResizeBilinear 使用 Bilinear (双线插值)算法,速度次之,但是锯齿会少很多。

func (*ImageAttr) ResizeNN

func (img *ImageAttr) ResizeNN(dstWidth, dstHeight int) *ImageAttr

ResizeNN 使用 NearestNeighbor 算法缩放图片,速度很快,但是会有锯齿。

type TJPixelFormat

type TJPixelFormat int

TJPixelFormat 像素存储的格式。

const (
	// TJPixelFormatRGB RGB
	TJPixelFormatRGB TJPixelFormat = C.TJPF_RGB
	// TJPixelFormatBGR BGR
	TJPixelFormatBGR TJPixelFormat = C.TJPF_BGR
	// TJPixelFormatRGBX RGBX
	TJPixelFormatRGBX TJPixelFormat = C.TJPF_RGBX
	// TJPixelFormatBGRX BGRX
	TJPixelFormatBGRX TJPixelFormat = C.TJPF_BGRX
	// TJPixelFormatXBGR XBGR
	TJPixelFormatXBGR TJPixelFormat = C.TJPF_XBGR
	// TJPixelFormatXRGB XRGB
	TJPixelFormatXRGB TJPixelFormat = C.TJPF_XRGB
	// TJPixelFormatGray gray
	TJPixelFormatGray TJPixelFormat = C.TJPF_GRAY
	// TJPixelFormatRGBA RGBA
	TJPixelFormatRGBA TJPixelFormat = C.TJPF_RGBA
	// TJPixelFormatBGRA BGRA
	TJPixelFormatBGRA TJPixelFormat = C.TJPF_BGRA
	// TJPixelFormatABGR ABGR
	TJPixelFormatABGR TJPixelFormat = C.TJPF_ABGR
	// TJPixelFormatARGB ARGB
	TJPixelFormatARGB TJPixelFormat = C.TJPF_ARGB
	// TJPixelFormatCMYK CMYK
	TJPixelFormatCMYK TJPixelFormat = C.TJPF_CMYK
	// TJPixelFormatUnknown 未知
	TJPixelFormatUnknown TJPixelFormat = C.TJPF_UNKNOWN
)

type TJSubSample

type TJSubSample int

TJSubSample 二次采样方法,一般是4:2:0采样。

const (
	// TjSubSample444 每个颜色分量对应1个像素点。
	TjSubSample444 TJSubSample = C.TJSAMP_444
	// TjSubSample422 每个颜色分量对应包含2x1个像素区块。
	TjSubSample422 TJSubSample = C.TJSAMP_422
	// TjSubSample420 每个颜色分量对应包含2x2个像素区块。
	TjSubSample420 TJSubSample = C.TJSAMP_420
	// TjSubSampleGray 只有明暗分量没有颜色分量
	TjSubSampleGray TJSubSample = C.TJSAMP_GRAY
	// TjSubSample440 每个颜色分量对应包含1x2个像素区块。
	// note: 4:4:0采样在turbojpeg中没有完全加速。
	TjSubSample440 TJSubSample = C.TJSAMP_440
	// TjSubSample411 每个颜色分量对应包含4x1个像素区块,和420大小一样的,但是更好地表现横向特征。
	TjSubSample411 TJSubSample = C.TJSAMP_411
)

Jump to

Keyboard shortcuts

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