fast, high peformance image processing libary.
blurry
provides image processing algorithms with halide-lang backend.
implements optimized processor for amd64 CPUs on Linux/macos
This is the result of using halide's benchamrk.
darwin/amd64 Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
src 320x240
BenchmarkJIT/cloneimg : 0.00788ms
BenchmarkJIT/convert_from_argb : 0.02375ms
BenchmarkJIT/convert_from_abgr : 0.03884ms
BenchmarkJIT/convert_from_bgra : 0.02470ms
BenchmarkJIT/convert_from_rabg : 0.03655ms
BenchmarkJIT/convert_from_yuv_420 : 0.03168ms
BenchmarkJIT/convert_from_yuv_444 : 0.02686ms
BenchmarkJIT/convert_to_yuv_420 : 0.06742ms
BenchmarkJIT/convert_to_yuv_444 : 0.07209ms
BenchmarkJIT/rotate0 : 0.00774ms
BenchmarkJIT/rotate90 : 0.02599ms
BenchmarkJIT/rotate180 : 0.00802ms
BenchmarkJIT/rotate270 : 0.02582ms
BenchmarkJIT/crop : 0.06126ms
BenchmarkJIT/scale : 0.13889ms
BenchmarkJIT/scale_box : 0.20598ms
BenchmarkJIT/scale_linear : 0.20440ms
BenchmarkJIT/scale_gaussian : 0.31444ms
BenchmarkJIT/blend_normal : 0.08443ms
BenchmarkJIT/blend_sub : 0.08398ms
BenchmarkJIT/blend_add : 0.08364ms
BenchmarkJIT/blend_diff : 0.08453ms
BenchmarkJIT/grayscale : 0.03687ms
BenchmarkJIT/invert : 0.03730ms
BenchmarkJIT/brightness : 0.04703ms
BenchmarkJIT/gammacorrection : 0.08013ms
BenchmarkJIT/contrast : 0.01549ms
BenchmarkJIT/boxblur : 0.11211ms
BenchmarkJIT/gaussianblur : 0.33058ms
BenchmarkJIT/blockmozaic : 0.27455ms
BenchmarkJIT/erosion : 0.11934ms
BenchmarkJIT/dilation : 0.12013ms
BenchmarkJIT/morphology_open : 0.10370ms
BenchmarkJIT/morphology_close : 0.10435ms
BenchmarkJIT/morphology_gradient : 0.07684ms
BenchmarkJIT/emboss : 0.04402ms
BenchmarkJIT/laplacian : 0.03192ms
BenchmarkJIT/highpass : 0.03847ms
BenchmarkJIT/gradient : 0.03322ms
BenchmarkJIT/edgedetect : 0.02705ms
BenchmarkJIT/sobel : 0.06276ms
BenchmarkJIT/canny : 0.29922ms
BenchmarkJIT/canny_dilate : 0.35114ms
BenchmarkJIT/canny_morphology_open : 0.44488ms
BenchmarkJIT/canny_morphology_close : 0.40522ms
BenchmarkJIT/match_template_sad : 5.75482ms
BenchmarkJIT/match_template_ssd : 4.48363ms
BenchmarkJIT/match_template_ncc : 8.32420ms
BenchmarkJIT/prepared_match_template_ncc : 6.22423ms
BenchmarkJIT/match_template_zncc : 12.73780ms
BenchmarkJIT/prepared_match_template_zncc : 11.38906ms
BenchmarkJIT/pcm16_decibel : 0.00257ms
Calling a library compiled by AOT(ahead-of-time) via cgo.
In cgo, due to the overhead of ffi calls(e.g.),
more complex operations will be optimized for CPU and become faster.
Also, the execution speed may be reduced by the overhead of multiple calls.
/D
is DisablePool, i.e. the benchmark when BufferPool is off.
goos: darwin
goarch: amd64
pkg: github.com/octu0/blurry/benchmark
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkBlur
BenchmarkBlur/bild/blur/Box
BenchmarkBlur/bild/blur/Box-8 154 7812824 ns/op 640402 B/op 11 allocs/op
BenchmarkBlur/bild/blur/Gaussian
BenchmarkBlur/bild/blur/Gaussian-8 333 3486751 ns/op 1262485 B/op 21 allocs/op
BenchmarkBlur/imaging/Blur
BenchmarkBlur/imaging/Blur-8 786 1520193 ns/op 793698 B/op 45 allocs/op
BenchmarkBlur/stackblur-go
BenchmarkBlur/stackblur-go-8 231 5147219 ns/op 925937 B/op 153609 allocs/op
BenchmarkBlur/libyuv/ARGBBlur
BenchmarkBlur/libyuv/ARGBBlur-8 1861 642486 ns/op 10182722 B/op 3 allocs/op
BenchmarkBlur/blurry/Boxblur
BenchmarkBlur/blurry/Boxblur-8 7257 178086 ns/op 88 B/op 2 allocs/op
BenchmarkBlur/blurry/Gaussianblur
BenchmarkBlur/blurry/Gaussianblur-8 5367 222615 ns/op 146 B/op 2 allocs/op
BenchmarkBlur/blurry/Boxblur/D
BenchmarkBlur/blurry/Boxblur/D-8 6093 201573 ns/op 311361 B/op 2 allocs/op
BenchmarkBlur/blurry/Gaussianblur/D
BenchmarkBlur/blurry/Gaussianblur/D-8 4629 257483 ns/op 311361 B/op 2 allocs/op
goos: darwin
goarch: amd64
pkg: github.com/octu0/blurry/benchmark
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkEdge
BenchmarkEdge/bild/EdgeDetection
BenchmarkEdge/bild/EdgeDetection-8 643 1858350 ns/op 631257 B/op 10 allocs/op
BenchmarkEdge/blurry/Edge
BenchmarkEdge/blurry/Edge-8 10000 100695 ns/op 311513 B/op 3 allocs/op
goos: darwin
goarch: amd64
pkg: github.com/octu0/blurry/benchmark
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkRotate
BenchmarkRotate/bild/Rotate/90
BenchmarkRotate/bild/Rotate/90-8 612 2080543 ns/op 1237046 B/op 115685 allocs/op
BenchmarkRotate/bild/Rotate/180
BenchmarkRotate/bild/Rotate/180-8 480 2355424 ns/op 1540311 B/op 153605 allocs/op
BenchmarkRotate/bild/Rotate/270
BenchmarkRotate/bild/Rotate/270-8 520 2061518 ns/op 1236932 B/op 115685 allocs/op
BenchmarkRotate/imaging/90
BenchmarkRotate/imaging/90-8 7918 130736 ns/op 314181 B/op 6 allocs/op
BenchmarkRotate/imaging/180
BenchmarkRotate/imaging/180-8 9654 138252 ns/op 313542 B/op 6 allocs/op
BenchmarkRotate/imaging/270
BenchmarkRotate/imaging/270-8 6972 163349 ns/op 314165 B/op 6 allocs/op
BenchmarkRotate/libyuv/ARGBRotate/90
BenchmarkRotate/libyuv/ARGBRotate/90-8 13423 81131 ns/op 311360 B/op 2 allocs/op
BenchmarkRotate/libyuv/ARGBRotate/180
BenchmarkRotate/libyuv/ARGBRotate/180-8 34771 34425 ns/op 311361 B/op 2 allocs/op
BenchmarkRotate/libyuv/ARGBRotate/270
BenchmarkRotate/libyuv/ARGBRotate/270-8 15904 78290 ns/op 311361 B/op 2 allocs/op
BenchmarkRotate/blurry/Rotate/90
BenchmarkRotate/blurry/Rotate/90-8 10000 109336 ns/op 311514 B/op 3 allocs/op
BenchmarkRotate/blurry/Rotate/180
BenchmarkRotate/blurry/Rotate/180-8 13102 89067 ns/op 311514 B/op 3 allocs/op
BenchmarkRotate/blurry/Rotate/270
BenchmarkRotate/blurry/Rotate/270-8 10000 124949 ns/op 311514 B/op 3 allocs/op
goos: darwin
goarch: amd64
pkg: github.com/octu0/blurry/benchmark
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
BenchmarkSobel
BenchmarkSobel/bild/Sobel
BenchmarkSobel/bild/Sobel-8 208 5707148 ns/op 2196784 B/op 32 allocs/op
BenchmarkSobel/libyuv/ARGBSobel
BenchmarkSobel/libyuv/ARGBSobel-8 16557 72320 ns/op 311361 B/op 2 allocs/op
BenchmarkSobel/blurry/Sobel
BenchmarkSobel/blurry/Sobel-8 9255 140586 ns/op 311515 B/op 3 allocs/op
See _benchmark for benchmarks of other methods and performance comparison with libyuv.
$ go get github.com/octu0/blurry
original image
rotation 0/90/180/270 clockwise
img, err := blurry.Rotate(input, blurry.Rotate90)
blurry.RotationMode |
Result |
---|---|
blurry.Rotate90 |
|
blurry.Rotate180 |
|
blurry.Rotate270 |
flipV(vertically) flipH(horizontally)
img, err := blurry.Flip(input, blurry.FlipVertical)
blurry.FlipMode |
Result |
---|---|
blurry.FlipVertical |
|
blurry.FlipHorizon |
crop x,y with crop_width,crop_height
img, err := blurry.Crop(input, image.Pt(175, 40), crop_width, crop_height)
original | x=175,y=40,cw=80,ch=50 |
---|---|
a.k.a. Resize resampling
img, err := blurry.Scale(input, scale_width, scale_height, blurry.ScaleFilterNone)
blurry.ScaleFilter |
Result |
---|---|
blurry.ScaleFilterNone |
|
blurry.ScaleFilterBox |
|
blurry.ScaleFilterLinear |
|
blurry.ScaleFilterGaussian |
img, err := blurry.Grayscale(input)
img, err := blurry.Invert(input)
img, err := blurry.Brightness(input, 1.5)
img, err := blurry.Gamma(input, 2.5)
img, err := blurry.Contrast(input, 0.525)
img, err := blurry.Boxblur(input, 11)
img, err := blurry.Gaussianblur(input, 5.0)
img, err := blurry.Blockmozaic(input, 10)
img, err := blurry.Erosion(input, 5)
img, err := blurry.Dilation(input, 8)
Morphology repeats Erode and Dilate N times.
size := 5
N := 2
img, err := blurry.Morphology(input, MorphOpen, size, N)
blurry.MorphologyMode |
Result |
---|---|
blurry.MorphologyOpen |
|
blurry.MorphologyClose |
|
blurry.MorphologyGradient |
img, err := blurry.Emboss(input)
img, err := blurry.Highpass(input)
img, err := blurry.Laplacian(input)
img, err := blurry.Gradient(input)
a.k.a. Edge Detection
img, err := blurry.Edge(input)
img, err := blurry.Sobel(input)
a.k.a. Canny Edge Detection
img, err := blurry.Canny(input, 250, 100)
max:250 min:100 |
max:400 min:10 |
---|---|
img, err := blurry.CannyWithDilate(input, 250, 100, 3)
max:250 min:100 dilate:3 |
max:250 min:150 dilate:4 |
---|---|
Pre-process morphology before applying Canny process.
mode := blurry.CannyMorphologyClose
morph_size := 5
dilate_size := 3
img, err := blurry.MorphologyCannyWithDilate(input, 250, 100, mode, morph_size, dilate_size);
blurry.CannyMorphologyMode |
Result |
---|---|
blurry.CannyMorphologyOpen |
|
blurry.CannyMorphologyClose |
SAD(Sum of Absolute Difference), SSD(Sum of Squared Difference), NCC(Normalized Cross Correlation) AND ZNCC(Zero means Normalized Cross Correlation) methods are available for template matching.
scores, err := blurry.MatchTemplateSAD(input, template, 1000)
filter | input | template | Result |
---|---|---|---|
none |
|||
grayscale |
|||
sobel |
|||
canny dilate:3 morph:open |
scores, err := blurry.MatchTemplateSSD(input, template, 1000)
filter | input | template | Result |
---|---|---|---|
none |
|||
grayscale |
|||
sobel |
|||
canny dilate:3 morph:open |
scores, err := blurry.MatchTemplateNCC(input, template, 0.1)
filter | input | template | Result |
---|---|---|---|
none |
|||
grayscale |
|||
sobel |
|||
canny dilate:3 morph:open |
Improve processing speed by pre-calculating part of NCC process.
p, err := blurry.PrepareNCCTemplate(template)
if err != nil {
panic(err)
}
defer blurry.FreePreparedNCCTemplate(p)
for _, img := range images {
scores, err := blurry.PreparedMatchTemplateNCC(img, p, 0.1)
if err != nil {
panic(err)
}
}
scores, err := blurry.MatchTemplateZNCC(input, template, 0.1)
filter | input | template | Result |
---|---|---|---|
none |
|||
grayscale |
|||
sobel |
|||
canny dilate:3 morph:open |
Improve processing speed by pre-calculating part of ZNCC process.
p, err := blurry.PrepareZNCCTemplate(template)
if err != nil {
panic(err)
}
defer blurry.FreePreparedZNCCTemplate(p)
for _, img := range images {
scores, err := blurry.PreparedMatchTemplateZNCC(img, p, 0.1)
if err != nil {
panic(err)
}
}
Extract contours based on the sobel filter for binarization.
In actual use, it is better to denoise the image before passing it through the sobel filter.
points, err := blurry.Contour(input, 100, 4)
threshold |
size | Result |
---|---|---|
100 | 4 | |
150 | 4 | |
200 | 4 | |
250 | 4 | |
100 | 2 | |
200 | 2 |
Blend input1 on input0.
img, err := blurry.Blend(input0, input1, image.Pt(76, 36), blurry.BlendNormal)
blurry.BlendMode |
Result |
---|---|
blurry.BlendNormal |
|
blurry.BlendSub |
|
blurry.BlendAdd |
|
blurry.BlendDiff |
blurry supports reading ARGB, ABGR, BGRA, YUV420 and YUV444.
It also supports YUV444 output.
img, err := blurry.ConvertFromARGB(input)
ColorModel | Method |
---|---|
ARGB | blurry.ConvertFromARGB(*image.RGBA) |
ABGR | blurry.ConvertFromABGR(*image.RGBA) |
BGRA | blurry.ConvertFromBGRA(*image.RGBA) |
RABG | blurry.ConvertFromRABG(*image.RGBA) |
img, err := blurry.ConvertFromYUV420(ycbcr)
Subsampling | Method |
---|---|
420 | blurry.ConvertFromYUV420(*image.YCbCr) |
444 | blurry.ConvertFromYUV444(*image.YCbCr) |
or byte slice can also be specified
var y,u,v []byte
var strideY,strideU,strideV int
var width, height int
img, err := blurry.ConvertFromYUV420Plane(y, u, v, strideY, strideU, strideV, width, height)
Subsampling | Method |
---|---|
420 | blurry.ConvertFromYUV420Plane(y,u,v []byte, int,int,int, w,h int) |
444 | blurry.ConvertFromYUV444Plane(y,u,v []byte, int,int,int, w,h int) |
ycbcr, err := blurry.ConvertToYUV444(rgba)
Subsampling | Method |
---|---|
420 | blurry.ConvertToYUV420(*image.RGBA) |
444 | blurry.ConvertToYUV444(*image.RGBA) |
Gets the decibel of given PCM16.
var data []byte
decibel, err := blurry.PCM16Decibel(data, length)
or
var input []int16
decibel, err := blurry.PCM16DecibelFromInt16(input)
Run it via docker.
Use docker run -v
to specify where to load the images and where to output them (/tmp
will be used as a temporary file).
$ mkdir myimagedir
$ mkdir myimageout
$ cp /from/img/path.png myimagedir/src.png
# grayscale
$ docker run --rm -it \
-v $PWD/myimagedir:/img \
-v $PWD/myimageout:/tmp \
blurry:1.0.0 grayscale -i /img/src.png
NAME:
blurry
USAGE:
blurry [global options] command [command options] [arguments...]
VERSION:
1.20.1
COMMANDS:
blend
blockmozaic
boxblur
brightness
canny
clone
contour
contrast
convert
convert_from_yuv
convert_to_yuv
crop
dilation
edge
emboss
erosion
flip
gamma
gaussianblur
gradient
grayscale
highpass
invert
laplacian
morphology
match_template
pcm16
rotate
scale
sobel
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--debug, -d debug mode
--verbose, -V verbose. more message
--help, -h show help
--version, -v print the version
When building, create a docker container with Halide(clang, llvm, etc). installed as the build environment.
$ make build-generator
Compile libruntime.a
and all kinds lib*_osx.a
or lib*_linu.a
to make static link.
$ make generate
Finally, generate a docker image if necessary.
$ make build
Set up configuration for macos to be able to run image filtering directly through Halide.
$ make setup-halide-runtime
genrun
package allows you to export images to temporary file and run image filtering directly.
$ go run cmd/genrun/main.go benchmark
MIT, see LICENSE file for details.