This commit is contained in:
root
2019-04-22 02:59:20 +00:00
commit beccf3fe43
25440 changed files with 4054998 additions and 0 deletions

29
vendor/code.google.com/p/graphics-go/.hgignore generated vendored Normal file
View File

@ -0,0 +1,29 @@
syntax:glob
.DS_Store
.git
.gitignore
*.[568ao]
*.ao
*.so
*.pyc
._*
.nfs.*
[568a].out
*~
*.orig
*.rej
*.exe
.*.swp
core
*.cgo*.go
*.cgo*.c
_cgo_*
_obj
_test
_testmain.go
build.out
test.out
y.tab.[ch]
syntax:regexp
^.*/core.[0-9]*$

11
vendor/code.google.com/p/graphics-go/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,11 @@
# This is the official list of Graphics-Go authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Google Inc.

31
vendor/code.google.com/p/graphics-go/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,31 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the Graphics-Go repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# The submission process automatically checks to make sure
# that people submitting code are listed in this file (by email address).
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate Contributor License Agreement, found here:
#
# http://code.google.com/legal/individual-cla-v1.0.html
# http://code.google.com/legal/corporate-cla-v1.0.html
#
# The agreement for individuals can be filled out on the web.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
# Names should be added to this file like so:
# Name <email address>
# Please keep the list sorted.
David Crawshaw <crawshaw@google.com>
Johan Euphrosine <proppy@google.com>
Nigel Tao <nigeltao@golang.org>

27
vendor/code.google.com/p/graphics-go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2011 The Graphics-Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/code.google.com/p/graphics-go/Makefile generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Copyright 2011 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
all: install
# The order matters: earlier packages may not depend on later ones.
DIRS=\
graphics
test:
cd graphics; GOMAXPROCS=2 gomake test
bench:
cd graphics; GOMAXPROCS=2 gomake bench
install clean nuke:
for dir in $(DIRS); do \
$(MAKE) -C $$dir $@ || exit 1; \
done

7
vendor/code.google.com/p/graphics-go/README generated vendored Normal file
View File

@ -0,0 +1,7 @@
This is a Graphics library for the Go programming language.
Unless otherwise noted, the graphics-go source files are distributed
under the BSD-style license found in the LICENSE file.
Contributions should follow the same procedure as for the Go project:
http://golang.org/doc/contribute.html

View File

@ -0,0 +1,52 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"affine.go",
"blur.go",
"rotate.go",
"scale.go",
"thumbnail.go",
],
importmap = "go-common/vendor/code.google.com/p/graphics-go/graphics",
importpath = "code.google.com/p/graphics-go/graphics",
visibility = ["//visibility:public"],
deps = [
"//vendor/code.google.com/p/graphics-go/graphics/convolve:go_default_library",
"//vendor/code.google.com/p/graphics-go/graphics/interp:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"blur_test.go",
"rotate_test.go",
"scale_test.go",
"shared_test.go",
"thumbnail_test.go",
],
embed = [":go_default_library"],
deps = ["//vendor/code.google.com/p/graphics-go/graphics/graphicstest:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/code.google.com/p/graphics-go/graphics/convolve:all-srcs",
"//vendor/code.google.com/p/graphics-go/graphics/detect:all-srcs",
"//vendor/code.google.com/p/graphics-go/graphics/graphicstest:all-srcs",
"//vendor/code.google.com/p/graphics-go/graphics/interp:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

15
vendor/code.google.com/p/graphics-go/graphics/Makefile generated vendored Normal file
View File

@ -0,0 +1,15 @@
# Copyright 2011 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=code.google.com/p/graphics-go/graphics
GOFILES=\
affine.go\
blur.go\
rotate.go\
scale.go\
thumbnail.go\
include $(GOROOT)/src/Make.pkg

174
vendor/code.google.com/p/graphics-go/graphics/affine.go generated vendored Normal file
View File

@ -0,0 +1,174 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/interp"
"errors"
"image"
"image/draw"
"math"
)
// I is the identity Affine transform matrix.
var I = Affine{
1, 0, 0,
0, 1, 0,
0, 0, 1,
}
// Affine is a 3x3 2D affine transform matrix.
// M(i,j) is Affine[i*3+j].
type Affine [9]float64
// Mul returns the multiplication of two affine transform matrices.
func (a Affine) Mul(b Affine) Affine {
return Affine{
a[0]*b[0] + a[1]*b[3] + a[2]*b[6],
a[0]*b[1] + a[1]*b[4] + a[2]*b[7],
a[0]*b[2] + a[1]*b[5] + a[2]*b[8],
a[3]*b[0] + a[4]*b[3] + a[5]*b[6],
a[3]*b[1] + a[4]*b[4] + a[5]*b[7],
a[3]*b[2] + a[4]*b[5] + a[5]*b[8],
a[6]*b[0] + a[7]*b[3] + a[8]*b[6],
a[6]*b[1] + a[7]*b[4] + a[8]*b[7],
a[6]*b[2] + a[7]*b[5] + a[8]*b[8],
}
}
func (a Affine) transformRGBA(dst *image.RGBA, src *image.RGBA, i interp.RGBA) error {
srcb := src.Bounds()
b := dst.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
sx, sy := a.pt(x, y)
if inBounds(srcb, sx, sy) {
c := i.RGBA(src, sx, sy)
off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
dst.Pix[off+0] = c.R
dst.Pix[off+1] = c.G
dst.Pix[off+2] = c.B
dst.Pix[off+3] = c.A
}
}
}
return nil
}
// Transform applies the affine transform to src and produces dst.
func (a Affine) Transform(dst draw.Image, src image.Image, i interp.Interp) error {
if dst == nil {
return errors.New("graphics: dst is nil")
}
if src == nil {
return errors.New("graphics: src is nil")
}
// RGBA fast path.
dstRGBA, dstOk := dst.(*image.RGBA)
srcRGBA, srcOk := src.(*image.RGBA)
interpRGBA, interpOk := i.(interp.RGBA)
if dstOk && srcOk && interpOk {
return a.transformRGBA(dstRGBA, srcRGBA, interpRGBA)
}
srcb := src.Bounds()
b := dst.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
sx, sy := a.pt(x, y)
if inBounds(srcb, sx, sy) {
dst.Set(x, y, i.Interp(src, sx, sy))
}
}
}
return nil
}
func inBounds(b image.Rectangle, x, y float64) bool {
if x < float64(b.Min.X) || x >= float64(b.Max.X) {
return false
}
if y < float64(b.Min.Y) || y >= float64(b.Max.Y) {
return false
}
return true
}
func (a Affine) pt(x0, y0 int) (x1, y1 float64) {
fx := float64(x0) + 0.5
fy := float64(y0) + 0.5
x1 = fx*a[0] + fy*a[1] + a[2]
y1 = fx*a[3] + fy*a[4] + a[5]
return x1, y1
}
// TransformCenter applies the affine transform to src and produces dst.
// Equivalent to
// a.CenterFit(dst, src).Transform(dst, src, i).
func (a Affine) TransformCenter(dst draw.Image, src image.Image, i interp.Interp) error {
if dst == nil {
return errors.New("graphics: dst is nil")
}
if src == nil {
return errors.New("graphics: src is nil")
}
return a.CenterFit(dst.Bounds(), src.Bounds()).Transform(dst, src, i)
}
// Scale produces a scaling transform of factors x and y.
func (a Affine) Scale(x, y float64) Affine {
return a.Mul(Affine{
1 / x, 0, 0,
0, 1 / y, 0,
0, 0, 1,
})
}
// Rotate produces a clockwise rotation transform of angle, in radians.
func (a Affine) Rotate(angle float64) Affine {
s, c := math.Sincos(angle)
return a.Mul(Affine{
+c, +s, +0,
-s, +c, +0,
+0, +0, +1,
})
}
// Shear produces a shear transform by the slopes x and y.
func (a Affine) Shear(x, y float64) Affine {
d := 1 - x*y
return a.Mul(Affine{
+1 / d, -x / d, 0,
-y / d, +1 / d, 0,
0, 0, 1,
})
}
// Translate produces a translation transform with pixel distances x and y.
func (a Affine) Translate(x, y float64) Affine {
return a.Mul(Affine{
1, 0, -x,
0, 1, -y,
0, 0, +1,
})
}
// Center produces the affine transform, centered around the provided point.
func (a Affine) Center(x, y float64) Affine {
return I.Translate(-x, -y).Mul(a).Translate(x, y)
}
// CenterFit produces the affine transform, centered around the rectangles.
// It is equivalent to
// I.Translate(-<center of src>).Mul(a).Translate(<center of dst>)
func (a Affine) CenterFit(dst, src image.Rectangle) Affine {
dx := float64(dst.Min.X) + float64(dst.Dx())/2
dy := float64(dst.Min.Y) + float64(dst.Dy())/2
sx := float64(src.Min.X) + float64(src.Dx())/2
sy := float64(src.Min.Y) + float64(src.Dy())/2
return I.Translate(-sx, -sy).Mul(a).Translate(dx, dy)
}

68
vendor/code.google.com/p/graphics-go/graphics/blur.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/convolve"
"errors"
"image"
"image/draw"
"math"
)
// DefaultStdDev is the default blurring parameter.
var DefaultStdDev = 0.5
// BlurOptions are the blurring parameters.
// StdDev is the standard deviation of the normal, higher is blurrier.
// Size is the size of the kernel. If zero, it is set to Ceil(6 * StdDev).
type BlurOptions struct {
StdDev float64
Size int
}
// Blur produces a blurred version of the image, using a Gaussian blur.
func Blur(dst draw.Image, src image.Image, opt *BlurOptions) error {
if dst == nil {
return errors.New("graphics: dst is nil")
}
if src == nil {
return errors.New("graphics: src is nil")
}
sd := DefaultStdDev
size := 0
if opt != nil {
sd = opt.StdDev
size = opt.Size
}
if size < 1 {
size = int(math.Ceil(sd * 6))
}
kernel := make([]float64, 2*size+1)
for i := 0; i <= size; i++ {
x := float64(i) / sd
x = math.Pow(1/math.SqrtE, x*x)
kernel[size-i] = x
kernel[size+i] = x
}
// Normalize the weights to sum to 1.0.
kSum := 0.0
for _, k := range kernel {
kSum += k
}
for i, k := range kernel {
kernel[i] = k / kSum
}
return convolve.Convolve(dst, src, &convolve.SeparableKernel{
X: kernel,
Y: kernel,
})
}

View File

@ -0,0 +1,207 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"image/color"
"testing"
_ "image/png"
)
var blurOneColorTests = []transformOneColorTest{
{
"1x1-blank", 1, 1, 1, 1,
&BlurOptions{0.83, 1},
[]uint8{0xff},
[]uint8{0xff},
},
{
"1x1-spreadblank", 1, 1, 1, 1,
&BlurOptions{0.83, 2},
[]uint8{0xff},
[]uint8{0xff},
},
{
"3x3-blank", 3, 3, 3, 3,
&BlurOptions{0.83, 2},
[]uint8{
0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
},
[]uint8{
0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
},
},
{
"3x3-dot", 3, 3, 3, 3,
&BlurOptions{0.34, 1},
[]uint8{
0x00, 0x00, 0x00,
0x00, 0xff, 0x00,
0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x03, 0x00,
0x03, 0xf2, 0x03,
0x00, 0x03, 0x00,
},
},
{
"5x5-dot", 5, 5, 5, 5,
&BlurOptions{0.34, 1},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x03, 0xf2, 0x03, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
},
{
"5x5-dot-spread", 5, 5, 5, 5,
&BlurOptions{0.85, 1},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x20, 0x10, 0x00,
0x00, 0x20, 0x40, 0x20, 0x00,
0x00, 0x10, 0x20, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
},
{
"4x4-box", 4, 4, 4, 4,
&BlurOptions{0.34, 1},
[]uint8{
0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x03, 0x03, 0x00,
0x03, 0xf8, 0xf8, 0x03,
0x03, 0xf8, 0xf8, 0x03,
0x00, 0x03, 0x03, 0x00,
},
},
{
"5x5-twodots", 5, 5, 5, 5,
&BlurOptions{0.34, 1},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x96, 0x00, 0x96, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x02, 0x00,
0x02, 0x8e, 0x04, 0x8e, 0x02,
0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
},
},
}
func TestBlurOneColor(t *testing.T) {
for _, oc := range blurOneColorTests {
dst := oc.newDst()
src := oc.newSrc()
opt := oc.opt.(*BlurOptions)
if err := Blur(dst, src, opt); err != nil {
t.Fatal(err)
}
if !checkTransformTest(t, &oc, dst) {
continue
}
}
}
func TestBlurEmpty(t *testing.T) {
empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
if err := Blur(empty, empty, nil); err != nil {
t.Fatal(err)
}
}
func TestBlurGopher(t *testing.T) {
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
dst := image.NewRGBA(src.Bounds())
if err = Blur(dst, src, &BlurOptions{StdDev: 1.1}); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-blur.png")
if err != nil {
t.Fatal(err)
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
if err != nil {
t.Fatal(err)
}
}
func benchBlur(b *testing.B, bounds image.Rectangle) {
b.StopTimer()
// Construct a fuzzy image.
src := image.NewRGBA(bounds)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
src.SetRGBA(x, y, color.RGBA{
uint8(5 * x % 0x100),
uint8(7 * y % 0x100),
uint8((7*x + 5*y) % 0x100),
0xff,
})
}
}
dst := image.NewRGBA(bounds)
b.StartTimer()
for i := 0; i < b.N; i++ {
Blur(dst, src, &BlurOptions{0.84, 3})
}
}
func BenchmarkBlur400x400x3(b *testing.B) {
benchBlur(b, image.Rect(0, 0, 400, 400))
}
// Exactly twice the pixel count of 400x400.
func BenchmarkBlur400x800x3(b *testing.B) {
benchBlur(b, image.Rect(0, 0, 400, 800))
}
// Exactly twice the pixel count of 400x800
func BenchmarkBlur400x1600x3(b *testing.B) {
benchBlur(b, image.Rect(0, 0, 400, 1600))
}

View File

@ -0,0 +1,30 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["convolve.go"],
importmap = "go-common/vendor/code.google.com/p/graphics-go/graphics/convolve",
importpath = "code.google.com/p/graphics-go/graphics/convolve",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["convolve_test.go"],
embed = [":go_default_library"],
deps = ["//vendor/code.google.com/p/graphics-go/graphics/graphicstest:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,11 @@
# Copyright 2011 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=code.google.com/p/graphics-go/graphics/convolve
GOFILES=\
convolve.go\
include $(GOROOT)/src/Make.pkg

View File

@ -0,0 +1,274 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package convolve
import (
"errors"
"fmt"
"image"
"image/draw"
"math"
)
// clamp clamps x to the range [x0, x1].
func clamp(x, x0, x1 float64) float64 {
if x < x0 {
return x0
}
if x > x1 {
return x1
}
return x
}
// Kernel is a square matrix that defines a convolution.
type Kernel interface {
// Weights returns the square matrix of weights in row major order.
Weights() []float64
}
// SeparableKernel is a linearly separable, square convolution kernel.
// X and Y are the per-axis weights. Each slice must be the same length, and
// have an odd length. The middle element of each slice is the weight for the
// central pixel. For example, the horizontal Sobel kernel is:
// sobelX := &SeparableKernel{
// X: []float64{-1, 0, +1},
// Y: []float64{1, 2, 1},
// }
type SeparableKernel struct {
X, Y []float64
}
func (k *SeparableKernel) Weights() []float64 {
n := len(k.X)
w := make([]float64, n*n)
for y := 0; y < n; y++ {
for x := 0; x < n; x++ {
w[y*n+x] = k.X[x] * k.Y[y]
}
}
return w
}
// fullKernel is a square convolution kernel.
type fullKernel []float64
func (k fullKernel) Weights() []float64 { return k }
func kernelSize(w []float64) (size int, err error) {
size = int(math.Sqrt(float64(len(w))))
if size*size != len(w) {
return 0, errors.New("graphics: kernel is not square")
}
if size%2 != 1 {
return 0, errors.New("graphics: kernel size is not odd")
}
return size, nil
}
// NewKernel returns a square convolution kernel.
func NewKernel(w []float64) (Kernel, error) {
if _, err := kernelSize(w); err != nil {
return nil, err
}
return fullKernel(w), nil
}
func convolveRGBASep(dst *image.RGBA, src image.Image, k *SeparableKernel) error {
if len(k.X) != len(k.Y) {
return fmt.Errorf("graphics: kernel not square (x %d, y %d)", len(k.X), len(k.Y))
}
if len(k.X)%2 != 1 {
return fmt.Errorf("graphics: kernel length (%d) not odd", len(k.X))
}
radius := (len(k.X) - 1) / 2
// buf holds the result of vertically blurring src.
bounds := dst.Bounds()
width, height := bounds.Dx(), bounds.Dy()
buf := make([]float64, width*height*4)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
var r, g, b, a float64
// k0 is the kernel weight for the center pixel. This may be greater
// than kernel[0], near the boundary of the source image, to avoid
// vignetting.
k0 := k.X[radius]
// Add the pixels from above.
for i := 1; i <= radius; i++ {
f := k.Y[radius-i]
if y-i < bounds.Min.Y {
k0 += f
} else {
or, og, ob, oa := src.At(x, y-i).RGBA()
r += float64(or>>8) * f
g += float64(og>>8) * f
b += float64(ob>>8) * f
a += float64(oa>>8) * f
}
}
// Add the pixels from below.
for i := 1; i <= radius; i++ {
f := k.Y[radius+i]
if y+i >= bounds.Max.Y {
k0 += f
} else {
or, og, ob, oa := src.At(x, y+i).RGBA()
r += float64(or>>8) * f
g += float64(og>>8) * f
b += float64(ob>>8) * f
a += float64(oa>>8) * f
}
}
// Add the central pixel.
or, og, ob, oa := src.At(x, y).RGBA()
r += float64(or>>8) * k0
g += float64(og>>8) * k0
b += float64(ob>>8) * k0
a += float64(oa>>8) * k0
// Write to buf.
o := (y-bounds.Min.Y)*width*4 + (x-bounds.Min.X)*4
buf[o+0] = r
buf[o+1] = g
buf[o+2] = b
buf[o+3] = a
}
}
// dst holds the result of horizontally blurring buf.
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
var r, g, b, a float64
k0, off := k.X[radius], y*width*4+x*4
// Add the pixels from the left.
for i := 1; i <= radius; i++ {
f := k.X[radius-i]
if x-i < 0 {
k0 += f
} else {
o := off - i*4
r += buf[o+0] * f
g += buf[o+1] * f
b += buf[o+2] * f
a += buf[o+3] * f
}
}
// Add the pixels from the right.
for i := 1; i <= radius; i++ {
f := k.X[radius+i]
if x+i >= width {
k0 += f
} else {
o := off + i*4
r += buf[o+0] * f
g += buf[o+1] * f
b += buf[o+2] * f
a += buf[o+3] * f
}
}
// Add the central pixel.
r += buf[off+0] * k0
g += buf[off+1] * k0
b += buf[off+2] * k0
a += buf[off+3] * k0
// Write to dst, clamping to the range [0, 255].
dstOff := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
dst.Pix[dstOff+0] = uint8(clamp(r+0.5, 0, 255))
dst.Pix[dstOff+1] = uint8(clamp(g+0.5, 0, 255))
dst.Pix[dstOff+2] = uint8(clamp(b+0.5, 0, 255))
dst.Pix[dstOff+3] = uint8(clamp(a+0.5, 0, 255))
}
}
return nil
}
func convolveRGBA(dst *image.RGBA, src image.Image, k Kernel) error {
b := dst.Bounds()
bs := src.Bounds()
w := k.Weights()
size, err := kernelSize(w)
if err != nil {
return err
}
radius := (size - 1) / 2
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
if !image.Pt(x, y).In(bs) {
continue
}
var r, g, b, a, adj float64
for cy := y - radius; cy <= y+radius; cy++ {
for cx := x - radius; cx <= x+radius; cx++ {
factor := w[(cy-y+radius)*size+cx-x+radius]
if !image.Pt(cx, cy).In(bs) {
adj += factor
} else {
sr, sg, sb, sa := src.At(cx, cy).RGBA()
r += float64(sr>>8) * factor
g += float64(sg>>8) * factor
b += float64(sb>>8) * factor
a += float64(sa>>8) * factor
}
}
}
if adj != 0 {
sr, sg, sb, sa := src.At(x, y).RGBA()
r += float64(sr>>8) * adj
g += float64(sg>>8) * adj
b += float64(sb>>8) * adj
a += float64(sa>>8) * adj
}
off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
dst.Pix[off+0] = uint8(clamp(r+0.5, 0, 0xff))
dst.Pix[off+1] = uint8(clamp(g+0.5, 0, 0xff))
dst.Pix[off+2] = uint8(clamp(b+0.5, 0, 0xff))
dst.Pix[off+3] = uint8(clamp(a+0.5, 0, 0xff))
}
}
return nil
}
// Convolve produces dst by applying the convolution kernel k to src.
func Convolve(dst draw.Image, src image.Image, k Kernel) (err error) {
if dst == nil || src == nil || k == nil {
return nil
}
b := dst.Bounds()
dstRgba, ok := dst.(*image.RGBA)
if !ok {
dstRgba = image.NewRGBA(b)
}
switch k := k.(type) {
case *SeparableKernel:
err = convolveRGBASep(dstRgba, src, k)
default:
err = convolveRGBA(dstRgba, src, k)
}
if err != nil {
return err
}
if !ok {
draw.Draw(dst, b, dstRgba, b.Min, draw.Src)
}
return nil
}

View File

@ -0,0 +1,78 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package convolve
import (
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"reflect"
"testing"
_ "image/png"
)
func TestSeparableWeights(t *testing.T) {
sobelXFull := []float64{
-1, 0, 1,
-2, 0, 2,
-1, 0, 1,
}
sobelXSep := &SeparableKernel{
X: []float64{-1, 0, +1},
Y: []float64{1, 2, 1},
}
w := sobelXSep.Weights()
if !reflect.DeepEqual(w, sobelXFull) {
t.Errorf("got %v want %v", w, sobelXFull)
}
}
func TestConvolve(t *testing.T) {
kernFull, err := NewKernel([]float64{
0, 0, 0,
1, 1, 1,
0, 0, 0,
})
if err != nil {
t.Fatal(err)
}
kernSep := &SeparableKernel{
X: []float64{1, 1, 1},
Y: []float64{0, 1, 0},
}
src, err := graphicstest.LoadImage("../../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
b := src.Bounds()
sep := image.NewRGBA(b)
if err = Convolve(sep, src, kernSep); err != nil {
t.Fatal(err)
}
full := image.NewRGBA(b)
Convolve(full, src, kernFull)
err = graphicstest.ImageWithinTolerance(sep, full, 0x101)
if err != nil {
t.Fatal(err)
}
}
func TestConvolveNil(t *testing.T) {
if err := Convolve(nil, nil, nil); err != nil {
t.Fatal(err)
}
}
func TestConvolveEmpty(t *testing.T) {
empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
if err := Convolve(empty, empty, nil); err != nil {
t.Fatal(err)
}
}

View File

@ -0,0 +1,40 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"detect.go",
"doc.go",
"integral.go",
"opencv_parser.go",
"projector.go",
],
importmap = "go-common/vendor/code.google.com/p/graphics-go/graphics/detect",
importpath = "code.google.com/p/graphics-go/graphics/detect",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"detect_test.go",
"integral_test.go",
"opencv_parser_test.go",
"projector_test.go",
],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,15 @@
# Copyright 2011 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=code.google.com/p/graphics-go/graphics
GOFILES=\
detect.go\
doc.go\
integral.go\
opencv_parser.go\
projector.go\
include $(GOROOT)/src/Make.pkg

View File

@ -0,0 +1,133 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
"math"
)
// Feature is a Haar-like feature.
type Feature struct {
Rect image.Rectangle
Weight float64
}
// Classifier is a set of features with a threshold.
type Classifier struct {
Feature []Feature
Threshold float64
Left float64
Right float64
}
// CascadeStage is a cascade of classifiers.
type CascadeStage struct {
Classifier []Classifier
Threshold float64
}
// Cascade is a degenerate tree of Haar-like classifiers.
type Cascade struct {
Stage []CascadeStage
Size image.Point
}
// Match returns true if the full image is classified as an object.
func (c *Cascade) Match(m image.Image) bool {
return c.classify(newWindow(m))
}
// Find returns a set of areas of m that match the feature cascade c.
func (c *Cascade) Find(m image.Image) []image.Rectangle {
// TODO(crawshaw): Consider de-duping strategies.
matches := []image.Rectangle{}
w := newWindow(m)
b := m.Bounds()
origScale := c.Size
for s := origScale; s.X < b.Dx() && s.Y < b.Dy(); s = s.Add(s.Div(10)) {
// translate region and classify
tx := image.Pt(s.X/10, 0)
ty := image.Pt(0, s.Y/10)
for r := image.Rect(0, 0, s.X, s.Y).Add(b.Min); r.In(b); r = r.Add(ty) {
for r1 := r; r1.In(b); r1 = r1.Add(tx) {
if c.classify(w.subWindow(r1)) {
matches = append(matches, r1)
}
}
}
}
return matches
}
type window struct {
mi *integral
miSq *integral
rect image.Rectangle
invArea float64
stdDev float64
}
func (w *window) init() {
w.invArea = 1 / float64(w.rect.Dx()*w.rect.Dy())
mean := float64(w.mi.sum(w.rect)) * w.invArea
vr := float64(w.miSq.sum(w.rect))*w.invArea - mean*mean
if vr < 0 {
vr = 1
}
w.stdDev = math.Sqrt(vr)
}
func newWindow(m image.Image) *window {
mi, miSq := newIntegrals(m)
res := &window{
mi: mi,
miSq: miSq,
rect: m.Bounds(),
}
res.init()
return res
}
func (w *window) subWindow(r image.Rectangle) *window {
res := &window{
mi: w.mi,
miSq: w.miSq,
rect: r,
}
res.init()
return res
}
func (c *Classifier) classify(w *window, pr *projector) float64 {
s := 0.0
for _, f := range c.Feature {
s += float64(w.mi.sum(pr.rect(f.Rect))) * f.Weight
}
s *= w.invArea // normalize to maintain scale invariance
if s < c.Threshold*w.stdDev {
return c.Left
}
return c.Right
}
func (s *CascadeStage) classify(w *window, pr *projector) bool {
sum := 0.0
for _, c := range s.Classifier {
sum += c.classify(w, pr)
}
return sum >= s.Threshold
}
func (c *Cascade) classify(w *window) bool {
pr := newProjector(w.rect, image.Rectangle{image.Pt(0, 0), c.Size})
for _, s := range c.Stage {
if !s.classify(w, pr) {
return false
}
}
return true
}

View File

@ -0,0 +1,77 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
"image/draw"
"testing"
)
var (
c0 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(0, 0, 3, 4), Weight: 4.0},
},
Threshold: 0.2,
Left: 0.8,
Right: 0.2,
}
c1 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(3, 4, 4, 5), Weight: 4.0},
},
Threshold: 0.2,
Left: 0.8,
Right: 0.2,
}
c2 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(0, 0, 1, 1), Weight: +4.0},
Feature{Rect: image.Rect(0, 0, 2, 2), Weight: -1.0},
},
Threshold: 0.2,
Left: 0.8,
Right: 0.2,
}
)
func TestClassifier(t *testing.T) {
m := image.NewGray(image.Rect(0, 0, 20, 20))
b := m.Bounds()
draw.Draw(m, image.Rect(0, 0, 20, 20), image.White, image.ZP, draw.Src)
draw.Draw(m, image.Rect(3, 4, 4, 5), image.Black, image.ZP, draw.Src)
w := newWindow(m)
pr := newProjector(b, b)
if res := c0.classify(w, pr); res != c0.Right {
t.Errorf("c0 got %f want %f", res, c0.Right)
}
if res := c1.classify(w, pr); res != c1.Left {
t.Errorf("c1 got %f want %f", res, c1.Left)
}
if res := c2.classify(w, pr); res != c1.Left {
t.Errorf("c2 got %f want %f", res, c1.Left)
}
}
func TestClassifierScale(t *testing.T) {
m := image.NewGray(image.Rect(0, 0, 50, 50))
b := m.Bounds()
draw.Draw(m, image.Rect(0, 0, 8, 10), image.White, b.Min, draw.Src)
draw.Draw(m, image.Rect(8, 10, 10, 13), image.Black, b.Min, draw.Src)
w := newWindow(m)
pr := newProjector(b, image.Rect(0, 0, 20, 20))
if res := c0.classify(w, pr); res != c0.Right {
t.Errorf("scaled c0 got %f want %f", res, c0.Right)
}
if res := c1.classify(w, pr); res != c1.Left {
t.Errorf("scaled c1 got %f want %f", res, c1.Left)
}
if res := c2.classify(w, pr); res != c1.Left {
t.Errorf("scaled c2 got %f want %f", res, c1.Left)
}
}

View File

@ -0,0 +1,31 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package detect implements an object detector cascade.
The technique used is a degenerate tree of Haar-like classifiers, commonly
used for face detection. It is described in
P. Viola, M. Jones.
Rapid Object Detection using a Boosted Cascade of Simple Features, 2001
IEEE Conference on Computer Vision and Pattern Recognition
A Cascade can be constructed manually from a set of Classifiers in stages,
or can be loaded from an XML file in the OpenCV format with
classifier, _, err := detect.ParseOpenCV(r)
The classifier can be used to determine if a full image is detected as an
object using Detect
if classifier.Match(m) {
// m is an image of a face.
}
It is also possible to search an image for occurrences of an object
objs := classifier.Find(m)
*/
package detect

View File

@ -0,0 +1,93 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
"image/draw"
)
// integral is an image.Image-like structure that stores the cumulative
// sum of the preceding pixels. This allows for O(1) summation of any
// rectangular region within the image.
type integral struct {
// pix holds the cumulative sum of the image's pixels. The pixel at
// (x, y) starts at pix[(y-rect.Min.Y)*stride + (x-rect.Min.X)*1].
pix []uint64
stride int
rect image.Rectangle
}
func (p *integral) at(x, y int) uint64 {
return p.pix[(y-p.rect.Min.Y)*p.stride+(x-p.rect.Min.X)]
}
func (p *integral) sum(b image.Rectangle) uint64 {
c := p.at(b.Max.X-1, b.Max.Y-1)
inY := b.Min.Y > p.rect.Min.Y
inX := b.Min.X > p.rect.Min.X
if inY && inX {
c += p.at(b.Min.X-1, b.Min.Y-1)
}
if inY {
c -= p.at(b.Max.X-1, b.Min.Y-1)
}
if inX {
c -= p.at(b.Min.X-1, b.Max.Y-1)
}
return c
}
func (m *integral) integrate() {
b := m.rect
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
c := uint64(0)
if y > b.Min.Y && x > b.Min.X {
c += m.at(x-1, y)
c += m.at(x, y-1)
c -= m.at(x-1, y-1)
} else if y > b.Min.Y {
c += m.at(b.Min.X, y-1)
} else if x > b.Min.X {
c += m.at(x-1, b.Min.Y)
}
m.pix[(y-m.rect.Min.Y)*m.stride+(x-m.rect.Min.X)] += c
}
}
}
// newIntegrals returns the integral and the squared integral.
func newIntegrals(src image.Image) (*integral, *integral) {
b := src.Bounds()
srcg, ok := src.(*image.Gray)
if !ok {
srcg = image.NewGray(b)
draw.Draw(srcg, b, src, b.Min, draw.Src)
}
m := integral{
pix: make([]uint64, b.Max.Y*b.Max.X),
stride: b.Max.X,
rect: b,
}
mSq := integral{
pix: make([]uint64, b.Max.Y*b.Max.X),
stride: b.Max.X,
rect: b,
}
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
os := (y-b.Min.Y)*srcg.Stride + x - b.Min.X
om := (y-b.Min.Y)*m.stride + x - b.Min.X
c := uint64(srcg.Pix[os])
m.pix[om] = c
mSq.pix[om] = c * c
}
}
m.integrate()
mSq.integrate()
return &m, &mSq
}

View File

@ -0,0 +1,156 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"bytes"
"fmt"
"image"
"testing"
)
type integralTest struct {
x int
y int
src []uint8
res []uint8
}
var integralTests = []integralTest{
{
1, 1,
[]uint8{0x01},
[]uint8{0x01},
},
{
2, 2,
[]uint8{
0x01, 0x02,
0x03, 0x04,
},
[]uint8{
0x01, 0x03,
0x04, 0x0a,
},
},
{
4, 4,
[]uint8{
0x02, 0x03, 0x00, 0x01,
0x01, 0x02, 0x01, 0x05,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
},
[]uint8{
0x02, 0x05, 0x05, 0x06,
0x03, 0x08, 0x09, 0x0f,
0x04, 0x0a, 0x0c, 0x13,
0x05, 0x0c, 0x0f, 0x17,
},
},
}
func sprintBox(box []byte, width, height int) string {
buf := bytes.NewBuffer(nil)
i := 0
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
fmt.Fprintf(buf, " 0x%02x,", box[i])
i++
}
buf.WriteByte('\n')
}
return buf.String()
}
func TestIntegral(t *testing.T) {
for i, oc := range integralTests {
src := &image.Gray{
Pix: oc.src,
Stride: oc.x,
Rect: image.Rect(0, 0, oc.x, oc.y),
}
dst, _ := newIntegrals(src)
res := make([]byte, len(dst.pix))
for i, p := range dst.pix {
res[i] = byte(p)
}
if !bytes.Equal(res, oc.res) {
got := sprintBox(res, oc.x, oc.y)
want := sprintBox(oc.res, oc.x, oc.y)
t.Errorf("%d: got\n%s\n want\n%s", i, got, want)
}
}
}
func TestIntegralSum(t *testing.T) {
src := &image.Gray{
Pix: []uint8{
0x02, 0x03, 0x00, 0x01, 0x03,
0x01, 0x02, 0x01, 0x05, 0x05,
0x01, 0x01, 0x01, 0x01, 0x02,
0x01, 0x01, 0x01, 0x01, 0x07,
0x02, 0x01, 0x00, 0x03, 0x01,
},
Stride: 5,
Rect: image.Rect(0, 0, 5, 5),
}
img, _ := newIntegrals(src)
type sumTest struct {
rect image.Rectangle
sum uint64
}
var sumTests = []sumTest{
{image.Rect(0, 0, 1, 1), 2},
{image.Rect(0, 0, 2, 1), 5},
{image.Rect(0, 0, 1, 3), 4},
{image.Rect(1, 1, 3, 3), 5},
{image.Rect(2, 2, 4, 4), 4},
{image.Rect(4, 3, 5, 5), 8},
{image.Rect(2, 4, 3, 5), 0},
}
for _, st := range sumTests {
s := img.sum(st.rect)
if s != st.sum {
t.Errorf("%v: got %d want %d", st.rect, s, st.sum)
return
}
}
}
func TestIntegralSubImage(t *testing.T) {
m0 := &image.Gray{
Pix: []uint8{
0x02, 0x03, 0x00, 0x01, 0x03,
0x01, 0x02, 0x01, 0x05, 0x05,
0x01, 0x04, 0x01, 0x01, 0x02,
0x01, 0x02, 0x01, 0x01, 0x07,
0x02, 0x01, 0x09, 0x03, 0x01,
},
Stride: 5,
Rect: image.Rect(0, 0, 5, 5),
}
b := image.Rect(1, 1, 4, 4)
m1 := m0.SubImage(b)
mi0, _ := newIntegrals(m0)
mi1, _ := newIntegrals(m1)
sum0 := mi0.sum(b)
sum1 := mi1.sum(b)
if sum0 != sum1 {
t.Errorf("b got %d want %d", sum0, sum1)
}
r0 := image.Rect(2, 2, 4, 4)
sum0 = mi0.sum(r0)
sum1 = mi1.sum(r0)
if sum0 != sum1 {
t.Errorf("r0 got %d want %d", sum1, sum0)
}
}

View File

@ -0,0 +1,125 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"image"
"io"
"io/ioutil"
"strconv"
"strings"
)
type xmlFeature struct {
Rects []string `xml:"grp>feature>rects>grp"`
Tilted int `xml:"grp>feature>tilted"`
Threshold float64 `xml:"grp>threshold"`
Left float64 `xml:"grp>left_val"`
Right float64 `xml:"grp>right_val"`
}
type xmlStages struct {
Trees []xmlFeature `xml:"trees>grp"`
Stage_threshold float64 `xml:"stage_threshold"`
Parent int `xml:"parent"`
Next int `xml:"next"`
}
type opencv_storage struct {
Any struct {
XMLName xml.Name
Type string `xml:"type_id,attr"`
Size string `xml:"size"`
Stages []xmlStages `xml:"stages>grp"`
} `xml:",any"`
}
func buildFeature(r string) (f Feature, err error) {
var x, y, w, h int
var weight float64
_, err = fmt.Sscanf(r, "%d %d %d %d %f", &x, &y, &w, &h, &weight)
if err != nil {
return
}
f.Rect = image.Rect(x, y, x+w, y+h)
f.Weight = weight
return
}
func buildCascade(s *opencv_storage) (c *Cascade, name string, err error) {
if s.Any.Type != "opencv-haar-classifier" {
err = fmt.Errorf("got %s want opencv-haar-classifier", s.Any.Type)
return
}
name = s.Any.XMLName.Local
c = &Cascade{}
sizes := strings.Split(s.Any.Size, " ")
w, err := strconv.Atoi(sizes[0])
if err != nil {
return nil, "", err
}
h, err := strconv.Atoi(sizes[1])
if err != nil {
return nil, "", err
}
c.Size = image.Pt(w, h)
c.Stage = []CascadeStage{}
for _, stage := range s.Any.Stages {
cs := CascadeStage{
Classifier: []Classifier{},
Threshold: stage.Stage_threshold,
}
for _, tree := range stage.Trees {
if tree.Tilted != 0 {
err = errors.New("Cascade does not support tilted features")
return
}
cls := Classifier{
Feature: []Feature{},
Threshold: tree.Threshold,
Left: tree.Left,
Right: tree.Right,
}
for _, rect := range tree.Rects {
f, err := buildFeature(rect)
if err != nil {
return nil, "", err
}
cls.Feature = append(cls.Feature, f)
}
cs.Classifier = append(cs.Classifier, cls)
}
c.Stage = append(c.Stage, cs)
}
return
}
// ParseOpenCV produces a detection Cascade from an OpenCV XML file.
func ParseOpenCV(r io.Reader) (cascade *Cascade, name string, err error) {
// BUG(crawshaw): tag-based parsing doesn't seem to work with <_>
buf, err := ioutil.ReadAll(r)
if err != nil {
return
}
buf = bytes.Replace(buf, []byte("<_>"), []byte("<grp>"), -1)
buf = bytes.Replace(buf, []byte("</_>"), []byte("</grp>"), -1)
s := &opencv_storage{}
err = xml.Unmarshal(buf, s)
if err != nil {
return
}
return buildCascade(s)
}

View File

@ -0,0 +1,75 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
"os"
"reflect"
"testing"
)
var (
classifier0 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(0, 0, 3, 4), Weight: -1},
Feature{Rect: image.Rect(3, 4, 5, 6), Weight: 3.1},
},
Threshold: 0.03,
Left: 0.01,
Right: 0.8,
}
classifier1 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(3, 7, 17, 11), Weight: -3.2},
Feature{Rect: image.Rect(3, 9, 17, 11), Weight: 2.},
},
Threshold: 0.11,
Left: 0.03,
Right: 0.83,
}
classifier2 = Classifier{
Feature: []Feature{
Feature{Rect: image.Rect(1, 1, 3, 3), Weight: -1.},
Feature{Rect: image.Rect(3, 3, 5, 5), Weight: 2.5},
},
Threshold: 0.07,
Left: 0.2,
Right: 0.4,
}
cascade = Cascade{
Stage: []CascadeStage{
CascadeStage{
Classifier: []Classifier{classifier0, classifier1},
Threshold: 0.82,
},
CascadeStage{
Classifier: []Classifier{classifier2},
Threshold: 0.22,
},
},
Size: image.Pt(20, 20),
}
)
func TestParseOpenCV(t *testing.T) {
file, err := os.Open("../../testdata/opencv.xml")
if err != nil {
t.Fatal(err)
}
defer file.Close()
cascadeFile, name, err := ParseOpenCV(file)
if err != nil {
t.Fatal(err)
}
if name != "name_of_cascade" {
t.Fatalf("name: got %s want name_of_cascade", name)
}
if !reflect.DeepEqual(cascade, *cascadeFile) {
t.Errorf("got\n %v want\n %v", *cascadeFile, cascade)
}
}

View File

@ -0,0 +1,55 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
)
// projector allows projecting from a source Rectangle onto a target Rectangle.
type projector struct {
// rx, ry is the scaling factor.
rx, ry float64
// dx, dy is the translation factor.
dx, dy float64
// r is the clipping region of the target.
r image.Rectangle
}
// newProjector creates a Projector with source src and target dst.
func newProjector(dst image.Rectangle, src image.Rectangle) *projector {
return &projector{
rx: float64(dst.Dx()) / float64(src.Dx()),
ry: float64(dst.Dy()) / float64(src.Dy()),
dx: float64(dst.Min.X - src.Min.X),
dy: float64(dst.Min.Y - src.Min.Y),
r: dst,
}
}
// pt projects p from the source rectangle onto the target rectangle.
func (s *projector) pt(p image.Point) image.Point {
return image.Point{
clamp(s.rx*float64(p.X)+s.dx, s.r.Min.X, s.r.Max.X),
clamp(s.ry*float64(p.Y)+s.dy, s.r.Min.Y, s.r.Max.Y),
}
}
// rect projects r from the source rectangle onto the target rectangle.
func (s *projector) rect(r image.Rectangle) image.Rectangle {
return image.Rectangle{s.pt(r.Min), s.pt(r.Max)}
}
// clamp rounds and clamps o to the integer range [x0, x1].
func clamp(o float64, x0, x1 int) int {
x := int(o + 0.5)
if x < x0 {
return x0
}
if x > x1 {
return x1
}
return x
}

View File

@ -0,0 +1,49 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package detect
import (
"image"
"reflect"
"testing"
)
type projectorTest struct {
dst image.Rectangle
src image.Rectangle
pdst image.Rectangle
psrc image.Rectangle
}
var projectorTests = []projectorTest{
{
image.Rect(0, 0, 6, 6),
image.Rect(0, 0, 2, 2),
image.Rect(0, 0, 6, 6),
image.Rect(0, 0, 2, 2),
},
{
image.Rect(0, 0, 6, 6),
image.Rect(0, 0, 2, 2),
image.Rect(3, 3, 6, 6),
image.Rect(1, 1, 2, 2),
},
{
image.Rect(30, 30, 40, 40),
image.Rect(10, 10, 20, 20),
image.Rect(32, 33, 34, 37),
image.Rect(12, 13, 14, 17),
},
}
func TestProjector(t *testing.T) {
for i, tt := range projectorTests {
pr := newProjector(tt.dst, tt.src)
res := pr.rect(tt.psrc)
if !reflect.DeepEqual(res, tt.pdst) {
t.Errorf("%d: got %v want %v", i, res, tt.pdst)
}
}
}

View File

@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["graphicstest.go"],
importmap = "go-common/vendor/code.google.com/p/graphics-go/graphics/graphicstest",
importpath = "code.google.com/p/graphics-go/graphics/graphicstest",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,11 @@
# Copyright 2011 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=code.google.com/p/graphics-go/graphics/graphicstest
GOFILES=\
graphicstest.go\
include $(GOROOT)/src/Make.pkg

View File

@ -0,0 +1,112 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphicstest
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
"os"
)
// LoadImage decodes an image from a file.
func LoadImage(path string) (img image.Image, err error) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
img, _, err = image.Decode(file)
return
}
func delta(u0, u1 uint32) int {
d := int(u0) - int(u1)
if d < 0 {
return -d
}
return d
}
func withinTolerance(c0, c1 color.Color, tol int) bool {
r0, g0, b0, a0 := c0.RGBA()
r1, g1, b1, a1 := c1.RGBA()
r := delta(r0, r1)
g := delta(g0, g1)
b := delta(b0, b1)
a := delta(a0, a1)
return r <= tol && g <= tol && b <= tol && a <= tol
}
// ImageWithinTolerance checks that each pixel varies by no more than tol.
func ImageWithinTolerance(m0, m1 image.Image, tol int) error {
b0 := m0.Bounds()
b1 := m1.Bounds()
if !b0.Eq(b1) {
return errors.New(fmt.Sprintf("got bounds %v want %v", b0, b1))
}
for y := b0.Min.Y; y < b0.Max.Y; y++ {
for x := b0.Min.X; x < b0.Max.X; x++ {
c0 := m0.At(x, y)
c1 := m1.At(x, y)
if !withinTolerance(c0, c1, tol) {
e := fmt.Sprintf("got %v want %v at (%d, %d)", c0, c1, x, y)
return errors.New(e)
}
}
}
return nil
}
// SprintBox pretty prints the array as a hexidecimal matrix.
func SprintBox(box []byte, width, height int) string {
buf := bytes.NewBuffer(nil)
i := 0
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
fmt.Fprintf(buf, " 0x%02x,", box[i])
i++
}
buf.WriteByte('\n')
}
return buf.String()
}
// SprintImageR pretty prints the red channel of src. It looks like SprintBox.
func SprintImageR(src *image.RGBA) string {
w, h := src.Rect.Dx(), src.Rect.Dy()
i := 0
box := make([]byte, w*h)
for y := src.Rect.Min.Y; y < src.Rect.Max.Y; y++ {
for x := src.Rect.Min.X; x < src.Rect.Max.X; x++ {
off := (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
box[i] = src.Pix[off]
i++
}
}
return SprintBox(box, w, h)
}
// MakeRGBA returns an image with R, G, B taken from src.
func MakeRGBA(src []uint8, width int) *image.RGBA {
b := image.Rect(0, 0, width, len(src)/width)
ret := image.NewRGBA(b)
i := 0
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
ret.SetRGBA(x, y, color.RGBA{
R: src[i],
G: src[i],
B: src[i],
A: 0xff,
})
i++
}
}
return ret
}

View File

@ -0,0 +1,33 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"bilinear.go",
"doc.go",
"interp.go",
],
importmap = "go-common/vendor/code.google.com/p/graphics-go/graphics/interp",
importpath = "code.google.com/p/graphics-go/graphics/interp",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["bilinear_test.go"],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,13 @@
# Copyright 2012 The Graphics-Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=code.google.com/p/graphics-go/graphics/interp
GOFILES=\
bilinear.go\
doc.go\
interp.go\
include $(GOROOT)/src/Make.pkg

View File

@ -0,0 +1,206 @@
// Copyright 2012 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package interp
import (
"image"
"image/color"
"math"
)
// Bilinear implements bilinear interpolation.
var Bilinear Interp = bilinear{}
type bilinear struct{}
func (i bilinear) Interp(src image.Image, x, y float64) color.Color {
if src, ok := src.(*image.RGBA); ok {
return i.RGBA(src, x, y)
}
return bilinearGeneral(src, x, y)
}
func bilinearGeneral(src image.Image, x, y float64) color.Color {
p := findLinearSrc(src.Bounds(), x, y)
var fr, fg, fb, fa float64
var r, g, b, a uint32
r, g, b, a = src.At(p.low.X, p.low.Y).RGBA()
fr += float64(r) * p.frac00
fg += float64(g) * p.frac00
fb += float64(b) * p.frac00
fa += float64(a) * p.frac00
r, g, b, a = src.At(p.high.X, p.low.Y).RGBA()
fr += float64(r) * p.frac01
fg += float64(g) * p.frac01
fb += float64(b) * p.frac01
fa += float64(a) * p.frac01
r, g, b, a = src.At(p.low.X, p.high.Y).RGBA()
fr += float64(r) * p.frac10
fg += float64(g) * p.frac10
fb += float64(b) * p.frac10
fa += float64(a) * p.frac10
r, g, b, a = src.At(p.high.X, p.high.Y).RGBA()
fr += float64(r) * p.frac11
fg += float64(g) * p.frac11
fb += float64(b) * p.frac11
fa += float64(a) * p.frac11
var c color.RGBA64
c.R = uint16(fr + 0.5)
c.G = uint16(fg + 0.5)
c.B = uint16(fb + 0.5)
c.A = uint16(fa + 0.5)
return c
}
func (bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
p := findLinearSrc(src.Bounds(), x, y)
// Array offsets for the surrounding pixels.
off00 := offRGBA(src, p.low.X, p.low.Y)
off01 := offRGBA(src, p.high.X, p.low.Y)
off10 := offRGBA(src, p.low.X, p.high.Y)
off11 := offRGBA(src, p.high.X, p.high.Y)
var fr, fg, fb, fa float64
fr += float64(src.Pix[off00+0]) * p.frac00
fg += float64(src.Pix[off00+1]) * p.frac00
fb += float64(src.Pix[off00+2]) * p.frac00
fa += float64(src.Pix[off00+3]) * p.frac00
fr += float64(src.Pix[off01+0]) * p.frac01
fg += float64(src.Pix[off01+1]) * p.frac01
fb += float64(src.Pix[off01+2]) * p.frac01
fa += float64(src.Pix[off01+3]) * p.frac01
fr += float64(src.Pix[off10+0]) * p.frac10
fg += float64(src.Pix[off10+1]) * p.frac10
fb += float64(src.Pix[off10+2]) * p.frac10
fa += float64(src.Pix[off10+3]) * p.frac10
fr += float64(src.Pix[off11+0]) * p.frac11
fg += float64(src.Pix[off11+1]) * p.frac11
fb += float64(src.Pix[off11+2]) * p.frac11
fa += float64(src.Pix[off11+3]) * p.frac11
var c color.RGBA
c.R = uint8(fr + 0.5)
c.G = uint8(fg + 0.5)
c.B = uint8(fb + 0.5)
c.A = uint8(fa + 0.5)
return c
}
func (bilinear) Gray(src *image.Gray, x, y float64) color.Gray {
p := findLinearSrc(src.Bounds(), x, y)
// Array offsets for the surrounding pixels.
off00 := offGray(src, p.low.X, p.low.Y)
off01 := offGray(src, p.high.X, p.low.Y)
off10 := offGray(src, p.low.X, p.high.Y)
off11 := offGray(src, p.high.X, p.high.Y)
var fc float64
fc += float64(src.Pix[off00]) * p.frac00
fc += float64(src.Pix[off01]) * p.frac01
fc += float64(src.Pix[off10]) * p.frac10
fc += float64(src.Pix[off11]) * p.frac11
var c color.Gray
c.Y = uint8(fc + 0.5)
return c
}
type bilinearSrc struct {
// Top-left and bottom-right interpolation sources
low, high image.Point
// Fraction of each pixel to take. The 0 suffix indicates
// top/left, and the 1 suffix indicates bottom/right.
frac00, frac01, frac10, frac11 float64
}
func findLinearSrc(b image.Rectangle, sx, sy float64) bilinearSrc {
maxX := float64(b.Max.X)
maxY := float64(b.Max.Y)
minX := float64(b.Min.X)
minY := float64(b.Min.Y)
lowX := math.Floor(sx - 0.5)
lowY := math.Floor(sy - 0.5)
if lowX < minX {
lowX = minX
}
if lowY < minY {
lowY = minY
}
highX := math.Ceil(sx - 0.5)
highY := math.Ceil(sy - 0.5)
if highX >= maxX {
highX = maxX - 1
}
if highY >= maxY {
highY = maxY - 1
}
// In the variables below, the 0 suffix indicates top/left, and the
// 1 suffix indicates bottom/right.
// Center of each surrounding pixel.
x00 := lowX + 0.5
y00 := lowY + 0.5
x01 := highX + 0.5
y01 := lowY + 0.5
x10 := lowX + 0.5
y10 := highY + 0.5
x11 := highX + 0.5
y11 := highY + 0.5
p := bilinearSrc{
low: image.Pt(int(lowX), int(lowY)),
high: image.Pt(int(highX), int(highY)),
}
// Literally, edge cases. If we are close enough to the edge of
// the image, curtail the interpolation sources.
if lowX == highX && lowY == highY {
p.frac00 = 1.0
} else if sy-minY <= 0.5 && sx-minX <= 0.5 {
p.frac00 = 1.0
} else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
p.frac11 = 1.0
} else if sy-minY <= 0.5 || lowY == highY {
p.frac00 = x01 - sx
p.frac01 = sx - x00
} else if sx-minX <= 0.5 || lowX == highX {
p.frac00 = y10 - sy
p.frac10 = sy - y00
} else if maxY-sy <= 0.5 {
p.frac10 = x11 - sx
p.frac11 = sx - x10
} else if maxX-sx <= 0.5 {
p.frac01 = y11 - sy
p.frac11 = sy - y01
} else {
p.frac00 = (x01 - sx) * (y10 - sy)
p.frac01 = (sx - x00) * (y11 - sy)
p.frac10 = (x11 - sx) * (sy - y00)
p.frac11 = (sx - x10) * (sy - y01)
}
return p
}
// TODO(crawshaw): When we have inlining, consider func (p *RGBA) Off(x, y) int
func offRGBA(src *image.RGBA, x, y int) int {
return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
}
func offGray(src *image.Gray, x, y int) int {
return (y-src.Rect.Min.Y)*src.Stride + (x - src.Rect.Min.X)
}

View File

@ -0,0 +1,143 @@
// Copyright 2012 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package interp
import (
"image"
"image/color"
"testing"
)
type interpTest struct {
desc string
src []uint8
srcWidth int
x, y float64
expect uint8
}
func (p *interpTest) newSrc() *image.RGBA {
b := image.Rect(0, 0, p.srcWidth, len(p.src)/p.srcWidth)
src := image.NewRGBA(b)
i := 0
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
src.SetRGBA(x, y, color.RGBA{
R: p.src[i],
G: p.src[i],
B: p.src[i],
A: 0xff,
})
i++
}
}
return src
}
var interpTests = []interpTest{
{
desc: "center of a single white pixel should match that pixel",
src: []uint8{0x00},
srcWidth: 1,
x: 0.5,
y: 0.5,
expect: 0x00,
},
{
desc: "middle of a square is equally weighted",
src: []uint8{
0x00, 0xff,
0xff, 0x00,
},
srcWidth: 2,
x: 1.0,
y: 1.0,
expect: 0x80,
},
{
desc: "center of a pixel is just that pixel",
src: []uint8{
0x00, 0xff,
0xff, 0x00,
},
srcWidth: 2,
x: 1.5,
y: 0.5,
expect: 0xff,
},
{
desc: "asymmetry abounds",
src: []uint8{
0xaa, 0x11, 0x55,
0xff, 0x95, 0xdd,
},
srcWidth: 3,
x: 2.0,
y: 1.0,
expect: 0x76, // (0x11 + 0x55 + 0x95 + 0xdd) / 4
},
}
func TestBilinearRGBA(t *testing.T) {
for _, p := range interpTests {
src := p.newSrc()
// Fast path.
c := Bilinear.(RGBA).RGBA(src, p.x, p.y)
if c.R != c.G || c.R != c.B || c.A != 0xff {
t.Errorf("expect channels to match, got %v", c)
continue
}
if c.R != p.expect {
t.Errorf("%s: got 0x%02x want 0x%02x", p.desc, c.R, p.expect)
continue
}
// Standard Interp should use the fast path.
cStd := Bilinear.Interp(src, p.x, p.y)
if cStd != c {
t.Errorf("%s: standard mismatch got %v want %v", p.desc, cStd, c)
continue
}
// General case should match the fast path.
cGen := color.RGBAModel.Convert(bilinearGeneral(src, p.x, p.y))
r0, g0, b0, a0 := c.RGBA()
r1, g1, b1, a1 := cGen.RGBA()
if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
t.Errorf("%s: general case mismatch got %v want %v", p.desc, c, cGen)
continue
}
}
}
func TestBilinearSubImage(t *testing.T) {
b0 := image.Rect(0, 0, 4, 4)
src0 := image.NewRGBA(b0)
b1 := image.Rect(1, 1, 3, 3)
src1 := src0.SubImage(b1).(*image.RGBA)
src1.Set(1, 1, color.RGBA{0x11, 0, 0, 0xff})
src1.Set(2, 1, color.RGBA{0x22, 0, 0, 0xff})
src1.Set(1, 2, color.RGBA{0x33, 0, 0, 0xff})
src1.Set(2, 2, color.RGBA{0x44, 0, 0, 0xff})
tests := []struct {
x, y float64
want uint8
}{
{1, 1, 0x11},
{3, 1, 0x22},
{1, 3, 0x33},
{3, 3, 0x44},
{2, 2, 0x2b},
}
for _, p := range tests {
c := Bilinear.(RGBA).RGBA(src1, p.x, p.y)
if c.R != p.want {
t.Errorf("(%.0f, %.0f): got 0x%02x want 0x%02x", p.x, p.y, c.R, p.want)
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright 2012 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package interp implements image interpolation.
An interpolator provides the Interp interface, which can be used
to interpolate a pixel:
c := interp.Bilinear.Interp(src, 1.2, 1.8)
To interpolate a large number of RGBA or Gray pixels, an implementation
may provide a fast-path by implementing the RGBA or Gray interfaces.
i1, ok := i.(interp.RGBA)
if ok {
c := i1.RGBA(src, 1.2, 1.8)
// use c.R, c.G, etc
return
}
c := i.Interp(src, 1.2, 1.8)
// use generic color.Color
*/
package interp

View File

@ -0,0 +1,29 @@
// Copyright 2012 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package interp
import (
"image"
"image/color"
)
// Interp interpolates an image's color at fractional co-ordinates.
type Interp interface {
// Interp interpolates (x, y).
Interp(src image.Image, x, y float64) color.Color
}
// RGBA is a fast-path interpolation implementation for image.RGBA.
// It is common for an Interp to also implement RGBA.
type RGBA interface {
// RGBA interpolates (x, y).
RGBA(src *image.RGBA, x, y float64) color.RGBA
}
// Gray is a fast-path interpolation implementation for image.Gray.
type Gray interface {
// Gray interpolates (x, y).
Gray(src *image.Gray, x, y float64) color.Gray
}

View File

@ -0,0 +1,35 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/interp"
"errors"
"image"
"image/draw"
)
// RotateOptions are the rotation parameters.
// Angle is the angle, in radians, to rotate the image clockwise.
type RotateOptions struct {
Angle float64
}
// Rotate produces a rotated version of src, drawn onto dst.
func Rotate(dst draw.Image, src image.Image, opt *RotateOptions) error {
if dst == nil {
return errors.New("graphics: dst is nil")
}
if src == nil {
return errors.New("graphics: src is nil")
}
angle := 0.0
if opt != nil {
angle = opt.Angle
}
return I.Rotate(angle).TransformCenter(dst, src, interp.Bilinear)
}

View File

@ -0,0 +1,169 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"math"
"testing"
_ "image/png"
)
var rotateOneColorTests = []transformOneColorTest{
{
"onepixel-onequarter", 1, 1, 1, 1,
&RotateOptions{math.Pi / 2},
[]uint8{0xff},
[]uint8{0xff},
},
{
"onepixel-partial", 1, 1, 1, 1,
&RotateOptions{math.Pi * 2.0 / 3.0},
[]uint8{0xff},
[]uint8{0xff},
},
{
"onepixel-complete", 1, 1, 1, 1,
&RotateOptions{2 * math.Pi},
[]uint8{0xff},
[]uint8{0xff},
},
{
"even-onequarter", 2, 2, 2, 2,
&RotateOptions{math.Pi / 2.0},
[]uint8{
0xff, 0x00,
0x00, 0xff,
},
[]uint8{
0x00, 0xff,
0xff, 0x00,
},
},
{
"even-complete", 2, 2, 2, 2,
&RotateOptions{2.0 * math.Pi},
[]uint8{
0xff, 0x00,
0x00, 0xff,
},
[]uint8{
0xff, 0x00,
0x00, 0xff,
},
},
{
"line-partial", 3, 3, 3, 3,
&RotateOptions{math.Pi * 1.0 / 3.0},
[]uint8{
0x00, 0x00, 0x00,
0xff, 0xff, 0xff,
0x00, 0x00, 0x00,
},
[]uint8{
0xa2, 0x80, 0x00,
0x22, 0xff, 0x22,
0x00, 0x80, 0xa2,
},
},
{
"line-offset-partial", 3, 3, 3, 3,
&RotateOptions{math.Pi * 3 / 2},
[]uint8{
0x00, 0x00, 0x00,
0x00, 0xff, 0xff,
0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0xff, 0x00,
0x00, 0xff, 0x00,
0x00, 0x00, 0x00,
},
},
{
"dot-partial", 4, 4, 4, 4,
&RotateOptions{math.Pi},
[]uint8{
0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
},
[]uint8{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00,
},
},
}
func TestRotateOneColor(t *testing.T) {
for _, oc := range rotateOneColorTests {
src := oc.newSrc()
dst := oc.newDst()
if err := Rotate(dst, src, oc.opt.(*RotateOptions)); err != nil {
t.Errorf("rotate %s: %v", oc.desc, err)
continue
}
if !checkTransformTest(t, &oc, dst) {
continue
}
}
}
func TestRotateEmpty(t *testing.T) {
empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
if err := Rotate(empty, empty, nil); err != nil {
t.Fatal(err)
}
}
func TestRotateGopherSide(t *testing.T) {
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
srcb := src.Bounds()
dst := image.NewRGBA(image.Rect(0, 0, srcb.Dy(), srcb.Dx()))
if err := Rotate(dst, src, &RotateOptions{math.Pi / 2.0}); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-rotate-side.png")
if err != nil {
t.Fatal(err)
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
if err != nil {
t.Fatal(err)
}
}
func TestRotateGopherPartial(t *testing.T) {
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
srcb := src.Bounds()
dst := image.NewRGBA(image.Rect(0, 0, srcb.Dx(), srcb.Dy()))
if err := Rotate(dst, src, &RotateOptions{math.Pi / 3.0}); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-rotate-partial.png")
if err != nil {
t.Fatal(err)
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
if err != nil {
t.Fatal(err)
}
}

31
vendor/code.google.com/p/graphics-go/graphics/scale.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/interp"
"errors"
"image"
"image/draw"
)
// Scale produces a scaled version of the image using bilinear interpolation.
func Scale(dst draw.Image, src image.Image) error {
if dst == nil {
return errors.New("graphics: dst is nil")
}
if src == nil {
return errors.New("graphics: src is nil")
}
b := dst.Bounds()
srcb := src.Bounds()
if b.Empty() || srcb.Empty() {
return nil
}
sx := float64(b.Dx()) / float64(srcb.Dx())
sy := float64(b.Dy()) / float64(srcb.Dy())
return I.Scale(sx, sy).Transform(dst, src, interp.Bilinear)
}

View File

@ -0,0 +1,153 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"testing"
_ "image/png"
)
var scaleOneColorTests = []transformOneColorTest{
{
"down-half",
1, 1,
2, 2,
nil,
[]uint8{
0x80, 0x00,
0x00, 0x80,
},
[]uint8{
0x40,
},
},
{
"up-double",
4, 4,
2, 2,
nil,
[]uint8{
0x80, 0x00,
0x00, 0x80,
},
[]uint8{
0x80, 0x60, 0x20, 0x00,
0x60, 0x50, 0x30, 0x20,
0x20, 0x30, 0x50, 0x60,
0x00, 0x20, 0x60, 0x80,
},
},
{
"up-doublewidth",
4, 2,
2, 2,
nil,
[]uint8{
0x80, 0x00,
0x00, 0x80,
},
[]uint8{
0x80, 0x60, 0x20, 0x00,
0x00, 0x20, 0x60, 0x80,
},
},
{
"up-doubleheight",
2, 4,
2, 2,
nil,
[]uint8{
0x80, 0x00,
0x00, 0x80,
},
[]uint8{
0x80, 0x00,
0x60, 0x20,
0x20, 0x60,
0x00, 0x80,
},
},
{
"up-partial",
3, 3,
2, 2,
nil,
[]uint8{
0x80, 0x00,
0x00, 0x80,
},
[]uint8{
0x80, 0x40, 0x00,
0x40, 0x40, 0x40,
0x00, 0x40, 0x80,
},
},
}
func TestScaleOneColor(t *testing.T) {
for _, oc := range scaleOneColorTests {
dst := oc.newDst()
src := oc.newSrc()
if err := Scale(dst, src); err != nil {
t.Errorf("scale %s: %v", oc.desc, err)
continue
}
if !checkTransformTest(t, &oc, dst) {
continue
}
}
}
func TestScaleEmpty(t *testing.T) {
empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
if err := Scale(empty, empty); err != nil {
t.Fatal(err)
}
}
func TestScaleGopher(t *testing.T) {
dst := image.NewRGBA(image.Rect(0, 0, 100, 150))
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Error(err)
return
}
// Down-sample.
if err := Scale(dst, src); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-100x150.png")
if err != nil {
t.Error(err)
return
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
if err != nil {
t.Error(err)
return
}
// Up-sample.
dst = image.NewRGBA(image.Rect(0, 0, 500, 750))
if err := Scale(dst, src); err != nil {
t.Fatal(err)
}
cmp, err = graphicstest.LoadImage("../testdata/gopher-500x750.png")
if err != nil {
t.Error(err)
return
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
if err != nil {
t.Error(err)
return
}
}

View File

@ -0,0 +1,69 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"bytes"
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"image/color"
"testing"
)
type transformOneColorTest struct {
desc string
dstWidth int
dstHeight int
srcWidth int
srcHeight int
opt interface{}
src []uint8
res []uint8
}
func (oc *transformOneColorTest) newSrc() *image.RGBA {
b := image.Rect(0, 0, oc.srcWidth, oc.srcHeight)
src := image.NewRGBA(b)
i := 0
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
src.SetRGBA(x, y, color.RGBA{
R: oc.src[i],
G: oc.src[i],
B: oc.src[i],
A: oc.src[i],
})
i++
}
}
return src
}
func (oc *transformOneColorTest) newDst() *image.RGBA {
return image.NewRGBA(image.Rect(0, 0, oc.dstWidth, oc.dstHeight))
}
func checkTransformTest(t *testing.T, oc *transformOneColorTest, dst *image.RGBA) bool {
for ch := 0; ch < 4; ch++ {
i := 0
res := make([]byte, len(oc.res))
for y := 0; y < oc.dstHeight; y++ {
for x := 0; x < oc.dstWidth; x++ {
off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
res[i] = dst.Pix[off+ch]
i++
}
}
if !bytes.Equal(res, oc.res) {
got := graphicstest.SprintBox(res, oc.dstWidth, oc.dstHeight)
want := graphicstest.SprintBox(oc.res, oc.dstWidth, oc.dstHeight)
t.Errorf("%s: ch=%d\n got\n%s\n want\n%s", oc.desc, ch, got, want)
return false
}
}
return true
}

View File

@ -0,0 +1,41 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"image"
"image/draw"
)
// Thumbnail scales and crops src so it fits in dst.
func Thumbnail(dst draw.Image, src image.Image) error {
// Scale down src in the dimension that is closer to dst.
sb := src.Bounds()
db := dst.Bounds()
rx := float64(sb.Dx()) / float64(db.Dx())
ry := float64(sb.Dy()) / float64(db.Dy())
var b image.Rectangle
if rx < ry {
b = image.Rect(0, 0, db.Dx(), int(float64(sb.Dy())/rx))
} else {
b = image.Rect(0, 0, int(float64(sb.Dx())/ry), db.Dy())
}
buf := image.NewRGBA(b)
if err := Scale(buf, src); err != nil {
return err
}
// Crop.
// TODO(crawshaw): improve on center-alignment.
var pt image.Point
if rx < ry {
pt.Y = (b.Dy() - db.Dy()) / 2
} else {
pt.X = (b.Dx() - db.Dx()) / 2
}
draw.Draw(dst, db, buf, pt, draw.Src)
return nil
}

View File

@ -0,0 +1,53 @@
// Copyright 2011 The Graphics-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graphics
import (
"code.google.com/p/graphics-go/graphics/graphicstest"
"image"
"testing"
_ "image/png"
)
func TestThumbnailGopher(t *testing.T) {
dst := image.NewRGBA(image.Rect(0, 0, 80, 80))
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
if err := Thumbnail(dst, src); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-thumb-80x80.png")
if err != nil {
t.Fatal(err)
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
if err != nil {
t.Error(err)
}
}
func TestThumbnailLongGopher(t *testing.T) {
dst := image.NewRGBA(image.Rect(0, 0, 50, 150))
src, err := graphicstest.LoadImage("../testdata/gopher.png")
if err != nil {
t.Fatal(err)
}
if err := Thumbnail(dst, src); err != nil {
t.Fatal(err)
}
cmp, err := graphicstest.LoadImage("../testdata/gopher-thumb-50x150.png")
if err != nil {
t.Fatal(err)
}
err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
if err != nil {
t.Error(err)
}
}

View File

@ -0,0 +1 @@
defaultcc: golang-dev@googlegroups.com

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0"?>
<!--
Copyright 2011 The Graphics-Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<opencv_storage>
<name_of_cascade type_id="opencv-haar-classifier">
<size>20 20</size>
<stages>
<_>
<!-- stage 0 -->
<trees>
<_>
<!-- tree 0 -->
<_>
<!-- root node -->
<feature>
<rects>
<_>0 0 3 4 -1.</_>
<_>3 4 2 2 3.1</_></rects>
<tilted>0</tilted></feature>
<threshold>0.03</threshold>
<left_val>0.01</left_val>
<right_val>0.8</right_val>
</_>
</_>
<_>
<!-- tree 1 -->
<_>
<!-- root node -->
<feature>
<rects>
<_>3 7 14 4 -3.2</_>
<_>3 9 14 2 2.</_></rects>
<tilted>0</tilted></feature>
<threshold>0.11</threshold>
<left_val>0.03</left_val>
<right_val>0.83</right_val>
</_>
</_>
</trees>
<stage_threshold>0.82</stage_threshold>
<parent>-1</parent>
<next>-1</next>
</_>
<_>
<!-- stage 1 -->
<trees>
<_>
<!-- tree 0 -->
<_>
<!-- root node -->
<feature>
<rects>
<_>1 1 2 2 -1.</_>
<_>3 3 2 2 2.5</_></rects>
<tilted>0</tilted></feature>
<threshold>0.07</threshold>
<left_val>0.2</left_val>
<right_val>0.4</right_val>
</_>
</_>
</trees>
<stage_threshold>0.22</stage_threshold>
<parent>0</parent>
<next>-1</next>
</_>
</stages>
</name_of_cascade>
</opencv_storage>