提交 8ff8dc58 编写于 作者: A Anthony N. Simon

improving package documentation and variable naming

上级 e5987419
......@@ -6,7 +6,8 @@ import (
"math"
)
// Brightness returns a copy of the image with the adjusted brightness
// Brightness returns a copy of the image with the adjusted brightness.
// Percent is the relative amount of change to be applied (range -100.0 to 100.0).
func Brightness(src image.Image, percentChange float64) *image.RGBA {
fn := func(c color.RGBA) color.RGBA {
......
/*Package bild provides a collection of common image processing functions.
The input images must implement the image.Image interface and the functions return an *image.RGBA.
The aim of this project is simplicity in use and development over high performance, but most algorithms are designed to be efficient and make use of parallelism when available.
It is based on standard Go packages to reduce dependecy use and development abstractions.*/
package bild
import (
......@@ -10,9 +15,10 @@ type normColor struct {
r, g, b, a float64
}
// Add returns an image with the added color values of two images
func Add(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Add combines the foreground and background images by adding their values and
// returns the resulting image.
func Add(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
......@@ -35,9 +41,10 @@ func Add(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Multiply returns an image with the normalized color values of two images multiplied
func Multiply(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Multiply combines the foreground and background images by multiplying their
// normalized values and returns the resulting image.
func Multiply(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
......@@ -60,9 +67,10 @@ func Multiply(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Overlay returns an image that combines Multiply and Screen blend modes
func Overlay(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Overlay combines the foreground and background images by using Multiply when channel values < 0.5
// or using Screen otherwise and returns the resulting image.
func Overlay(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -102,9 +110,10 @@ func Overlay(a image.Image, b image.Image) *image.RGBA {
return dst
}
// SoftLight returns an image has the Soft Light blend mode applied
func SoftLight(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// SoftLight combines the foreground and background images by using Pegtop's Soft Light formula and
// returns the resulting image.
func SoftLight(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -127,9 +136,10 @@ func SoftLight(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Screen returns an image that has the screen blend mode applied
func Screen(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Screen combines the foreground and background images by inverting, multiplying and inverting the output.
// The result is a brighter image which is then returned.
func Screen(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -152,9 +162,10 @@ func Screen(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Difference returns an image which represts the absolute difference between the input images
func Difference(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Difference calculates the absolute difference between the foreground and background images and
// returns the resulting image.
func Difference(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -177,9 +188,10 @@ func Difference(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Divide returns an image after Dividing the value of A by B
func Divide(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Divide combines the foreground and background images by diving the values from the background
// by the foreground and returns the resulting image.
func Divide(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -202,9 +214,10 @@ func Divide(a image.Image, b image.Image) *image.RGBA {
return dst
}
// ColorBurn returns an image after applying a Color Burn blend mode
func ColorBurn(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// ColorBurn combines the foreground and background images by dividing the inverted
// background by the foreground image and then inverting the result which is then returned.
func ColorBurn(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -227,9 +240,10 @@ func ColorBurn(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Exclusion returns an image after applying a Exclusion blend mode
func Exclusion(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Exclusion combines the foreground and background images applying the Exclusion blend mode and
// returns the resulting image.
func Exclusion(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -252,9 +266,10 @@ func Exclusion(a image.Image, b image.Image) *image.RGBA {
return dst
}
// ColorDodge returns an image after applying a Color Dodge blend mode
func ColorDodge(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// ColorDodge combines the foreground and background images by dividing background by the
// inverted foreground image and returns the result.
func ColorDodge(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -277,9 +292,10 @@ func ColorDodge(a image.Image, b image.Image) *image.RGBA {
return dst
}
// LinearBurn returns an image after applying a Linear Burn blend mode
func LinearBurn(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// LinearBurn combines the foreground and background images by adding them and
// then substracting 255 (1.0 in normalized scale). The resulting image is then returned.
func LinearBurn(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
......@@ -302,9 +318,10 @@ func LinearBurn(a image.Image, b image.Image) *image.RGBA {
return dst
}
// LinearLight returns an image after applying a Linear Light blend mode
func LinearLight(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// LinearLight combines the foreground and background images by a mix of a Linear Dodge and
// Linear Burn operation. The resulting image is then returned.
func LinearLight(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
......@@ -327,9 +344,10 @@ func LinearLight(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Substract returns an image after substracting the value of B from A
func Substract(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Substract combines the foreground and background images by substracting the background from the
// foreground. The result is then returned.
func Substract(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
......@@ -354,10 +372,10 @@ func Substract(a image.Image, b image.Image) *image.RGBA {
// Opacity returns an image which blends the two input images by the percentage provided.
// Percent must be of range 0 <= percent <= 1.0
func Opacity(a image.Image, b image.Image, percent float64) *image.RGBA {
func Opacity(bg image.Image, fg image.Image, percent float64) *image.RGBA {
percent = clampFloat64(percent, 0, 1.0)
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R) / 255
g0 := float64(c0.G) / 255
......@@ -380,9 +398,10 @@ func Opacity(a image.Image, b image.Image, percent float64) *image.RGBA {
return dst
}
// Darken returns an image which has the respective darker pixel from each input image
func Darken(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Darken combines the foreground and background images by picking the darkest value per channel
// for each pixel. The result is then returned.
func Darken(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
b0 := float64(c0.B)
......@@ -404,9 +423,10 @@ func Darken(a image.Image, b image.Image) *image.RGBA {
return dst
}
// Lighten returns an image which has the respective brighter pixel from each input image
func Lighten(a image.Image, b image.Image) *image.RGBA {
dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
// Lighten combines the foreground and background images by picking the brightest value per channel
// for each pixel. The result is then returned.
func Lighten(bg image.Image, fg image.Image) *image.RGBA {
dst := blendOperation(bg, fg, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {
r0 := float64(c0.R)
g0 := float64(c0.G)
b0 := float64(c0.B)
......@@ -428,15 +448,15 @@ func Lighten(a image.Image, b image.Image) *image.RGBA {
return dst
}
func blendOperation(a image.Image, b image.Image, fn func(color.RGBA, color.RGBA) color.RGBA) *image.RGBA {
func blendOperation(bg image.Image, fg image.Image, fn func(color.RGBA, color.RGBA) color.RGBA) *image.RGBA {
// Currently only equal size images are supported
if a.Bounds() != b.Bounds() {
if bg.Bounds() != fg.Bounds() {
panic("blend operation: only equal size images are supported")
}
bounds := a.Bounds()
srcA := CloneAsRGBA(a)
srcB := CloneAsRGBA(b)
bounds := bg.Bounds()
srcA := CloneAsRGBA(bg)
srcB := CloneAsRGBA(fg)
dst := image.NewRGBA(bounds)
......
......@@ -5,7 +5,8 @@ import (
"math"
)
// BoxBlur returns a blurred (average) version of the image
// BoxBlur returns a blurred (average) version of the image.
// Radius must be larger than 0.
func BoxBlur(src image.Image, radius float64) *image.RGBA {
if radius <= 0 {
return CloneAsRGBA(src)
......@@ -20,11 +21,11 @@ func BoxBlur(src image.Image, radius float64) *image.RGBA {
}
}
return Convolute(src, k.Normalized(), 0)
return Convolute(src, k.Normalized(), 0, true)
}
// GaussianBlur returns a smoothly blurred version of the image using
// a Gaussian function
// a Gaussian function. Radius must be larger than 0.
func GaussianBlur(src image.Image, radius float64) *image.RGBA {
if radius <= 0 {
return CloneAsRGBA(src)
......@@ -39,5 +40,5 @@ func GaussianBlur(src image.Image, radius float64) *image.RGBA {
}
}
return Convolute(src, k.Normalized(), 0)
return Convolute(src, k.Normalized(), 0, true)
}
......@@ -6,15 +6,15 @@ import (
"math"
)
// ConvolutionMatrix interface for use as an image Kernel
// ConvolutionMatrix interface for use as an image Kernel.
type ConvolutionMatrix interface {
At(x, y int) float64
Sum() float64
Normalized() ConvolutionMatrix
Diameter() int
Length() int
}
// NewKernel returns a kernel of the provided size
// NewKernel returns a kernel of the provided size.
func NewKernel(diameter int) *Kernel {
matrix := make([][]float64, diameter)
for i := 0; i < diameter; i++ {
......@@ -23,15 +23,15 @@ func NewKernel(diameter int) *Kernel {
return &Kernel{matrix}
}
// Kernel is used as a convolution matrix
// Kernel is used as a convolution matrix.
type Kernel struct {
Matrix [][]float64
}
// Sum returns the cumulative value of the matrix
// Sum returns the cumulative value of the matrix.
func (k *Kernel) Sum() float64 {
var sum float64
diameter := k.Diameter()
diameter := k.Length()
for x := 0; x < diameter; x++ {
for y := 0; y < diameter; y++ {
sum += k.Matrix[x][y]
......@@ -40,14 +40,14 @@ func (k *Kernel) Sum() float64 {
return sum
}
// Normalized returns a new Kernel with normalized values
// Normalized returns a new Kernel with normalized values.
func (k *Kernel) Normalized() ConvolutionMatrix {
sum := k.Sum()
diameter := k.Diameter()
nk := NewKernel(diameter)
length := k.Length()
nk := NewKernel(length)
for x := 0; x < diameter; x++ {
for y := 0; y < diameter; y++ {
for x := 0; x < length; x++ {
for y := 0; y < length; y++ {
nk.Matrix[x][y] = k.Matrix[x][y] / sum
}
}
......@@ -55,22 +55,23 @@ func (k *Kernel) Normalized() ConvolutionMatrix {
return nk
}
// Diameter returns the row/column length for the kernel
func (k *Kernel) Diameter() int {
// Length returns the row/column length for the kernel.
func (k *Kernel) Length() int {
return len(k.Matrix)
}
// At returns the matrix value at position x, y
// At returns the matrix value at position x, y.
func (k *Kernel) At(x, y int) float64 {
return k.Matrix[x][y]
}
// String returns the string representation of the matrix.
func (k *Kernel) String() string {
result := ""
size := k.Diameter()
for x := 0; x < size; x++ {
length := k.Length()
for x := 0; x < length; x++ {
result += fmt.Sprintf("\n")
for y := 0; y < size; y++ {
for y := 0; y < length; y++ {
result += fmt.Sprintf("%-8.4f", k.Matrix[x][y])
}
}
......@@ -78,24 +79,35 @@ func (k *Kernel) String() string {
}
// Convolute applies a convolution matrix (kernel) to an image.
// It wraps the image for indices outside of image dimensions
func Convolute(img image.Image, k ConvolutionMatrix, bias float64) *image.RGBA {
// If wrap is set to true, indices outside of image dimensions will be taken from the opposite side,
// otherwise the pixel at that index will be skipped.
func Convolute(img image.Image, k ConvolutionMatrix, bias float64, wrap bool) *image.RGBA {
bounds := img.Bounds()
src := CloneAsRGBA(img)
dst := image.NewRGBA(bounds)
w, h := bounds.Max.X, bounds.Max.Y
diameter := k.Diameter()
kernelLength := k.Length()
parallelize(h, func(start, end int) {
for x := 0; x < w; x++ {
for y := start; y < end; y++ {
var r, g, b, a float64
for kx := 0; kx < diameter; kx++ {
for ky := 0; ky < diameter; ky++ {
ix := (x - diameter/2 + kx + w) % (w)
iy := (y - diameter/2 + ky + h) % h
for kx := 0; kx < kernelLength; kx++ {
for ky := 0; ky < kernelLength; ky++ {
var ix, iy int
if wrap {
ix = (x - kernelLength/2 + kx + w) % w
iy = (y - kernelLength/2 + ky + h) % h
} else {
ix = x - kernelLength/2 + kx
iy = y - kernelLength/2 + ky
if ix < 0 || ix >= w || iy < 0 || iy >= h {
continue
}
}
ipos := iy*dst.Stride + ix*4
kvalue := k.At(kx, ky)
......
......@@ -6,7 +6,7 @@ import (
"math"
)
// Invert returns a negated version of the image
// Invert returns a negated version of the image.
func Invert(src image.Image) *image.RGBA {
fn := func(c color.RGBA) color.RGBA {
return color.RGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
......@@ -17,7 +17,8 @@ func Invert(src image.Image) *image.RGBA {
return img
}
// Grayscale returns a copy of the image in Grayscale using the weights: 0.3R + 0.6G + 0.1B
// Grayscale returns a copy of the image in Grayscale using the weights
// 0.3R + 0.6G + 0.1B as a heuristic.
func Grayscale(src image.Image) *image.RGBA {
fn := func(c color.RGBA) color.RGBA {
......@@ -36,7 +37,7 @@ func Grayscale(src image.Image) *image.RGBA {
return img
}
// EdgeDetection returns a copy of the image with it's edges marked
// EdgeDetection returns a copy of the image with it's edges highlighted.
func EdgeDetection(src image.Image, radius float64) *image.RGBA {
if radius <= 0 {
return CloneAsRGBA(src)
......@@ -55,10 +56,11 @@ func EdgeDetection(src image.Image, radius float64) *image.RGBA {
}
}
return Convolute(src, k, 0)
return Convolute(src, k, 0, false)
}
// Emboss returns a copy of the image with a 3D shadow effect
// Emboss returns a copy of the image in which each pixel has been
// replaced either by a highlight or a shadow representation.
func Emboss(src image.Image) *image.RGBA {
k := Kernel{[][]float64{
{-1, -1, 0},
......@@ -66,10 +68,10 @@ func Emboss(src image.Image) *image.RGBA {
{0, 1, 1},
}}
return Convolute(src, &k, 128)
return Convolute(src, &k, 128, false)
}
// Sobel returns an image emphasising edges using an approximation to the Sobel–Feldman operator
// Sobel returns an image emphasising edges using an approximation to the Sobel–Feldman operator.
func Sobel(src image.Image) *image.RGBA {
hk := Kernel{[][]float64{
......@@ -84,13 +86,14 @@ func Sobel(src image.Image) *image.RGBA {
{-1, 0, 1},
}}
vSobel := Convolute(src, &vk, 0)
hSobel := Convolute(src, &hk, 0)
vSobel := Convolute(src, &vk, 0, false)
hSobel := Convolute(src, &hk, 0, false)
return Add(Multiply(vSobel, vSobel), Multiply(hSobel, hSobel))
}
// Median returns a new image in which each pixel is the mean of it's neighbors
// Median returns a new image in which each pixel is the median of it's neighbors.
// Size sets the amount of neighbors to be searched.
func Median(img image.Image, size int) *image.RGBA {
bounds := img.Bounds()
src := CloneAsRGBA(img)
......
......@@ -2,7 +2,7 @@ package bild
import "image"
// FlipH returns a horizontally flipped version of the image
// FlipH returns a horizontally flipped version of the image.
func FlipH(img image.Image) *image.RGBA {
bounds := img.Bounds()
src := CloneAsRGBA(img)
......@@ -28,7 +28,7 @@ func FlipH(img image.Image) *image.RGBA {
return dst
}
// FlipV returns a vertically flipped version of the image
// FlipV returns a vertically flipped version of the image.
func FlipV(img image.Image) *image.RGBA {
bounds := img.Bounds()
src := CloneAsRGBA(img)
......
......@@ -13,12 +13,19 @@ import (
// Format is used to identify the image encoding type
type Format int
// Supported image encoding types
const (
JPEG = iota
PNG
)
// Open loads and decodes an image from a file
// Open loads and decodes an image from a file and returns it.
//
// Usage example:
// // Encode an image to a writer in PNG format,
// // returns an error if something went wrong
// img, err := Open("exampleName")
//
func Open(filename string) (image.Image, error) {
f, err := os.Open(filename)
if err != nil {
......@@ -34,7 +41,13 @@ func Open(filename string) (image.Image, error) {
return img, nil
}
// Encode writes an image in the specified format
// Encode writes an image in the specified format.
//
// Usage example:
// // Encode an image to a writer in PNG format,
// // returns an error if something went wrong
// err := Encode(outFile, img, Format.PNG)
//
func Encode(w io.Writer, img image.Image, format Format) error {
var err error
......@@ -49,6 +62,12 @@ func Encode(w io.Writer, img image.Image, format Format) error {
}
// Save creates a file and writes to it an image in the specified format
//
// Usage example:
// // Save an image to a file in PNG format,
// // returns an error if something went wrong
// err := Save("exampleName", img, Format.PNG)
//
func Save(filename string, img image.Image, format Format) error {
filename = strings.TrimSuffix(filename, filepath.Ext(filename))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册