Skip to content

Instantly share code, notes, and snippets.

@ajstarks
Last active April 15, 2022 15:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajstarks/492f1b4bcb02a03ab288 to your computer and use it in GitHub Desktop.
Save ajstarks/492f1b4bcb02a03ab288 to your computer and use it in GitHub Desktop.
// gift: command line interface to Go image filtering toolkit
// please go to http://github.com/ajstarks/gift for the latest
package main
import (
"flag"
"fmt"
"image"
"image/color"
"image/jpeg"
"image/png"
"io"
"os"
"github.com/disintegration/gift"
)
var (
blurvalue, brvalue, contvalue, hvalue, satvalue, gammavalue, sepiavalue float64
gray, neg, xpose, xverse, fliph, flipv, emboss, edge bool
res, cropspec, sigspec, unsharp string
rotvalue, minvalue, maxvalue, meanvalue, medvalue int
)
func main() {
flag.Float64Var(&blurvalue, "blur", 0, "blur value")
flag.Float64Var(&brvalue, "brightness", -200, "brightness value (-100, 100)")
flag.Float64Var(&hvalue, "hue", -200, "hue value (-180, 180)")
flag.Float64Var(&contvalue, "contrast", -200, "contrast value (-100, 100)")
flag.Float64Var(&satvalue, "saturation", -200, "saturation value (-100, 500)")
flag.Float64Var(&gammavalue, "gamma", 0, "gamma value")
flag.Float64Var(&sepiavalue, "sepia", -1, "sepia percentage (0-100)")
flag.IntVar(&rotvalue, "rotate", 0, "rotate specified degrees counter-clockwise")
flag.IntVar(&maxvalue, "max", 0, "local maximum (kernel size)")
flag.IntVar(&minvalue, "min", 0, "local minimum (kernel size)")
flag.IntVar(&medvalue, "median", 0, "local median filter (kernel size)")
flag.IntVar(&meanvalue, "mean", 0, "local mean filter (kernel size)")
flag.BoolVar(&flipv, "flipv", false, "flip vertical")
flag.BoolVar(&fliph, "fliph", false, "flip horizontal")
flag.BoolVar(&gray, "gray", false, "grayscale")
flag.BoolVar(&neg, "invert", false, "invert")
flag.BoolVar(&xpose, "transpose", false, "flip horizontally and rotate 90° counter-clockwise")
flag.BoolVar(&xverse, "transverse", false, " flips vertically and rotate 90° counter-clockwise")
flag.BoolVar(&emboss, "emboss", false, "emboss")
flag.BoolVar(&edge, "edge", false, "edge")
flag.StringVar(&res, "resize", "", "resize w,h")
flag.StringVar(&cropspec, "crop", "", "crop x1,y1,x2,y2")
flag.StringVar(&sigspec, "sigmoid", "", "sigmoid contrast (midpoint,factor)")
flag.StringVar(&unsharp, "unsharp", "", "unsharp mask (sigma,amount,threshold)")
flag.Parse()
var f io.Reader = os.Stdin
var ferr error
var fname string
if len(flag.Args()) > 0 {
fname = flag.Args()[0]
f, ferr = os.Open(fname)
if ferr != nil {
fmt.Fprintf(os.Stderr, "%v\n", ferr)
os.Exit(1)
}
}
src, format, ierr := image.Decode(f)
if ierr != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", fname, ierr)
os.Exit(2)
}
g := gift.New() // initial state
// stack filters when command flags are set (not default values)
if blurvalue > 0 {
g.Add(gift.GaussianBlur(float32(blurvalue)))
}
// brightness
if brvalue >= -100 && brvalue <= 100 {
g.Add(gift.Brightness(float32(brvalue)))
}
// hue
if hvalue >= -180 && hvalue <= 180 {
g.Add(gift.Hue(float32(hvalue)))
}
// contrast
if contvalue >= -100 && contvalue <= 100 {
g.Add(gift.Contrast(float32(contvalue)))
}
// saturation
if satvalue >= -100 && satvalue <= 500 {
g.Add(gift.Saturation(float32(satvalue)))
}
// gamma
if gammavalue > 0 {
g.Add(gift.Gamma(float32(gammavalue)))
}
// sepia
if sepiavalue >= 0 && sepiavalue <= 100 {
g.Add(gift.Sepia(float32(sepiavalue)))
}
// median
if medvalue > 0 && medvalue%1 == 0 {
g.Add(gift.Median(medvalue, true))
}
// mean
if meanvalue > 0 && meanvalue%1 == 0 {
g.Add(gift.Mean(meanvalue, true))
}
// minimum
if minvalue > 0 && minvalue%1 == 0 {
g.Add(gift.Minimum(minvalue, true))
}
// maximum
if maxvalue > 0 && maxvalue%1 == 0 {
g.Add(gift.Maximum(maxvalue, true))
}
// rotate
if rotvalue > 0 && rotvalue <= 360 {
switch rotvalue {
case 90:
g.Add(gift.Rotate90())
case 180:
g.Add(gift.Rotate180())
case 270:
g.Add(gift.Rotate270())
default:
g.Add(gift.Rotate(float32(rotvalue), color.White, gift.LinearInterpolation))
}
}
// grayscale
if gray {
g.Add(gift.Grayscale())
}
// invert
if neg {
g.Add(gift.Invert())
}
// transpose
if xpose {
g.Add(gift.Transpose())
}
// transverse
if xverse {
g.Add(gift.Transverse())
}
// flip horizontal
if fliph {
g.Add(gift.FlipHorizontal())
}
// flip vertical
if flipv {
g.Add(gift.FlipVertical())
}
// emboss
if emboss {
g.Add(gift.Convolution(
[]float32{-1, -1, 0, -1, 1, 1, 0, 1, 1},
false, false, false, 0.0))
}
// edge detections
if edge {
g.Add(gift.Convolution(
[]float32{-1, -1, -1, -1, 8, -1, -1, -1, -1},
false, false, false, 0.0))
}
// resize
if len(res) > 0 {
var w, h int
nr, err := fmt.Sscanf(res, "%d,%d", &w, &h)
if nr != 2 || err != nil {
fmt.Fprintf(os.Stderr, "use: -resize width,height\n")
os.Exit(3)
}
g.Add(gift.Resize(w, h, gift.LanczosResampling))
}
// crop
if len(cropspec) > 0 {
var x1, y1, x2, y2 int
nr, err := fmt.Sscanf(cropspec, "%d,%d,%d,%d", &x1, &y1, &x2, &y2)
if nr != 4 || err != nil {
fmt.Fprintf(os.Stderr, "use: -crop x1,y1,x2,y2\n")
os.Exit(4)
}
g.Add(gift.Crop(image.Rect(x1, y1, x2, y2)))
}
// unsharp
if len(unsharp) > 0 {
var sigma, amount, threshold float32
nr, err := fmt.Sscanf(unsharp, "%g,%g,%g", &sigma, &amount, &threshold)
if nr != 3 || err != nil {
fmt.Fprintf(os.Stderr, "use: -unsharp sigma,amount,threshold\n")
os.Exit(5)
}
g.Add(gift.UnsharpMask(sigma, amount, threshold))
}
// sigmoid
if len(sigspec) > 0 {
var midpoint, factor float32
nr, err := fmt.Sscanf(sigspec, "%g,%g", &midpoint, &factor)
if nr != 2 || err != nil {
fmt.Fprintf(os.Stderr, "use: -sigma midpoint,factor\n")
os.Exit(6)
}
g.Add(gift.Sigmoid(midpoint, factor))
}
// make the filtered image, writing to stdout
dst := image.NewRGBA(g.Bounds(src.Bounds()))
g.Draw(dst, src)
switch format {
case "png":
png.Encode(os.Stdout, dst)
case "jpeg":
jpeg.Encode(os.Stdout, dst, nil)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment