Skip to content

Comparing libyuv and blurry

Yusuke Hata edited this page Apr 4, 2021 · 3 revisions

libyuv.ARGBBlur vs blurry.Boxblur

libyuv cgo wrapper for ARGBBlur

In preparation for calling libyuv with cgo, I have created a wrapper as below. *image.NewRGBA in go's ARGBBlur func, but I think it would be better to pass []byte slice as an argument.

/*
#cgo CFLAGS: -I/path/to/libyuv/include
#cgo LDFLAGS: -L/path/to/libyuv/lib -lyuv
#include "planar_functions.h"
*/
import "C"
import (
	"fmt"
	"image"
	"unsafe"
)

//int ARGBBlur(const uint8_t* src_argb,
//             int src_stride_argb,
//             uint8_t* dst_argb,
//             int dst_stride_argb,
//             int32_t* dst_cumsum,
//             int dst_stride32_cumsum,
//             int width,
//             int height,
//             int radius);
//
func ARGBBlur(src *image.RGBA, width, height int, radius int) (*image.RGBA, error) {
	dst := image.NewRGBA(image.Rect(0, 0, width, height))

	cumsum := make([]int, width*(height+1)*16)
	stride := width * 4
	ret := C.ARGBBlur(
		(*C.uchar)(&src.Pix[0]),
		C.int(stride),
		(*C.uchar)(&dst.Pix[0]),
		C.int(stride),
		(*C.int)(unsafe.Pointer(&cumsum[0])),
		C.int(stride),
		C.int(width),
		C.int(height),
		C.int(radius),
	)
	if ret != 0 {
		return nil, fmt.Errorf("failed to call ARGBBlur:%d", ret)
	}
	return dst, nil
}

libyuv.ARGBBlur output

out271442001

This result of calling it with radius of 5 as below.

out, err := ARGBBlur(img, width, height, 5)
if err != nil {
	panic(err.Error())
}

blurry.BoxBlur output

out008981973

This result of calling it with size of 11 as below.

out, err := blurry.Boxblur(img, 11)
if err != nil {
	panic(err.Error())
}

Blur Benchmarks

blurry uses DisablePool because of []byte buffer pool implementation of sync.Pool.

I tried it with code below.

func main() {
	N := 100
	benchmark_libyuv(N)
	benchmark_blurry(N)
}

func benchmark_libyuv(N int) {
	d, err := png.Decode(bytes.NewReader(data))
	if err != nil {
		panic(err.Error())
	}
	img := convert(d)
	width, height := wh(img)

	t := time.Now()
	for i := 0; i < N; i += 1 {
		_, _ = ARGBBlur(img, width, height, 5)
	}
	fmt.Println("ARGBBlur", time.Since(t))
}

func benchmark_blurry(N int) {
	d, err := png.Decode(bytes.NewReader(data))
	if err != nil {
		panic(err.Error())
	}
	img := convert(d)

	blurry.DisablePool()

	t := time.Now()
	for i := 0; i < N; i += 1 {
		_, _ = blurry.Boxblur(img, 11)
	}
	fmt.Println("BoxBlur", time.Since(t))
}

results.

ARGBBlur 76.697866ms
BoxBlur 142.577851ms

libyuv.ARGBSobel vs blurry.Sobel

libyuv cgo wrapper for ARGBSobel

I have created a wrapper as below.

/*
#cgo CFLAGS: -I/path/to/libyuv/include
#cgo LDFLAGS: -L/path/to/libyuv/lib -lyuv
#include "planar_functions.h"
*/
import "C"
import (
	"fmt"
	"image"
)

//int ARGBSobel(const uint8_t* src_argb,
//              int src_stride_argb,
//              uint8_t* dst_argb,
//              int dst_stride_argb,
//              int width,
//              int height);
func ARGBSobel(src *image.RGBA, width, height int) (*image.RGBA, error) {
	dst := image.NewRGBA(image.Rect(0, 0, width, height))

	stride := width * 4
	ret := C.ARGBSobel(
		(*C.uchar)(&src.Pix[0]),
		C.int(stride),
		(*C.uchar)(&dst.Pix[0]),
		C.int(stride),
		C.int(width),
		C.int(height),
	)
	if ret != 0 {
		return nil, fmt.Errorf("failed to call ARGBSobel:%d", ret)
	}
	return dst, nil
}

libyuv.ARGBSobel output

out682708729

out, err := ARGBSobel(img, width, height)
if err != nil {
	panic(err.Error())
}

blurry.Sobel output

out839030788

out, err := blurry.Sobel(img)
if err != nil {
	panic(err.Error())
}

Sobel Benchmarks

func main() {
	N := 100
	benchmark_libyuv(N)
	benchmark_blurry(N)
}

func benchmark_libyuv(N int) {
	d, err := png.Decode(bytes.NewReader(data))
	if err != nil {
		panic(err.Error())
	}
	img := convert(d)
	width, height := wh(img)

	t := time.Now()
	for i := 0; i < N; i += 1 {
		_, _ = ARGBSobel(img, width, height)
	}
	fmt.Println("ARGBSobel", time.Since(t))
}

func benchmark_blurry(N int) {
	d, err := png.Decode(bytes.NewReader(data))
	if err != nil {
		panic(err.Error())
	}
	img := convert(d)

	blurry.DisablePool()

	t := time.Now()
	for i := 0; i < N; i += 1 {
		_, _ = blurry.Sobel(img)
	}
	fmt.Println("Sobel", time.Since(t))
}

results.

ARGBSobel 8.393158ms
Sobel 21.816918ms