Created
April 7, 2013 17:19
-
-
Save devi/5331398 to your computer and use it in GitHub Desktop.
My first learing by doing in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A small utility to resize images inside folder (including subfolder). | |
package main | |
import ( | |
"fmt" | |
"flag" | |
"math" | |
"os" | |
"io/ioutil" | |
"os/exec" | |
"bitbucket.org/kardianos/osext" | |
"log" | |
"strconv" | |
"path/filepath" | |
"strings" | |
"runtime" | |
"camlistore.org/pkg/images" | |
"image/jpeg" | |
"image/png" | |
_ "code.google.com/p/vp8-go/webp" | |
_ "code.google.com/p/go.image/bmp" | |
_ "code.google.com/p/go.image/tiff" | |
) | |
const RESIZER_VERSION = "0.0.1" | |
var ( | |
itsme string | |
flagVersion = flag.Bool("v", false, "Display software version") | |
flagMaxWidth = flag.Float64("mw", 1024, "Maximum width (in pixel)") | |
flagMaxHeight = flag.Float64("mh", 768, "Maximum height (in pixel)") | |
flagOutputFormat = flag.String("f", "jpeg", "Output image format (currently supported jpeg and png only)") | |
flagSrcPath = flag.String("s", "", "Source path to be processed") | |
flagDstPath = flag.String("d", "", "Destination path to save the results") | |
flagUseImagemagick = flag.Bool("i", false, "Use Imagemagick if aplicable, fallback to built in function.\nDefault command: convert -resize WidthxHeight /path/to/ImageFilename.ext -auto-orient -filter Lanczos -sampling-factor 1x1 -unsharp 1.5x1+0.7+0.02 -quality 90 -set filename:mysize %wx%h \"/path/to/output/ImageFilename_%[filename:mysize].jpeg\"") | |
flagCustomIM = flag.String("im", "", "Replace default parameter if using ImageMagick.\nReplaced params:-filter Lanczos -sampling-factor 1x1 -unsharp 1.5x1+0.7+0.02 -quality 90") | |
flagRecursive = flag.Bool("r", true, "Resize image recursively") | |
) | |
func init() { | |
itsme, _ = osext.Executable() | |
} | |
func main() { | |
flag.Parse() | |
if *flagVersion { | |
fmt.Printf("Resizer v%v\nDesigned to resize images inside a folder (including subfolder).\nAny question mailto: devi[dot]mandiri[at]gmail[dot]com\nUsage parameter:\n", RESIZER_VERSION) | |
flag.PrintDefaults() | |
return | |
} | |
if *flagMaxWidth <= 0 || *flagMaxHeight <= 0 { | |
log.Fatal("Maximum Width and Maximum Height must be specified\n") | |
} | |
if *flagSrcPath == "" { | |
var err error | |
*flagSrcPath, err = osext.ExecutableFolder() | |
if err != nil { | |
log.Fatal("Source path must be specified\n") | |
} | |
} | |
// if *flagDstPath == "" { | |
// *flagDstPath = *flagSrcPath | |
// } | |
// make sure the path is clean | |
*flagSrcPath = filepath.Clean(*flagSrcPath) | |
//*flagDstPath = filepath.Clean(*flagDstPath) | |
// test path | |
if !TestPath(*flagSrcPath) { | |
log.Fatalf("Folder does not exists: %v", *flagSrcPath) | |
} | |
if *flagDstPath != "" { | |
if !TestPath(*flagDstPath) { | |
log.Fatalf("Folder does not exists: %v", *flagDstPath) | |
} | |
} | |
*flagOutputFormat = strings.ToLower(*flagOutputFormat) | |
useBuiltIn := true | |
if *flagUseImagemagick { | |
// check ImageMagick | |
if runtime.GOOS == "windows" { | |
// just to make sure it's not a shit software | |
out, err := exec.Command("convert", "-version").Output() | |
if err == nil { | |
if strings.Contains(string(out), "http://www.imagemagick.org") { | |
useBuiltIn = false | |
} | |
} | |
} else { | |
_, err := exec.LookPath("convert") | |
if err == nil { | |
useBuiltIn = false | |
} | |
} | |
} else { | |
if *flagOutputFormat != "jpeg" && *flagOutputFormat != "png" { | |
log.Fatal("Output format only supported JPEG and PNG\n") | |
} | |
} | |
allowedExt := []string{".jpg", ".jpeg", ".bmp", ".tiff", ".png", ".webp"} | |
// collect images before execution | |
pics := []string{} | |
Walk(*flagSrcPath, func(imgPath string, finfo os.FileInfo, err error) error { | |
if imgPath != itsme && !finfo.IsDir() { | |
ext := strings.ToLower(filepath.Ext(finfo.Name())) | |
if !matchInArray(allowedExt, ext) { | |
return nil | |
} | |
pics = AppendIfMissing(pics, imgPath) | |
} | |
return nil | |
}, *flagRecursive) | |
for _, pic := range pics { | |
fmt.Printf("Resizing %v\n", pic) | |
fName := filepath.Base(pic) | |
fDest := *flagDstPath; | |
if fDest == "" || !TestPath(fDest) { | |
fDest = strings.TrimRight(pic, fName) | |
} | |
if useBuiltIn { | |
err := ResizeImage(pic, fName, fDest) | |
if err !=nil { | |
fmt.Printf("Error: %v\n", err) | |
} | |
} else { | |
err := UseImagemagick(pic, fName, fDest) | |
if err != nil { | |
fmt.Printf("Error: %v\n", err) | |
} | |
} | |
} | |
} | |
// adjusted from filepath.Walk | |
func Walk(root string, walkFn filepath.WalkFunc, recursive bool) error { | |
info, err := os.Lstat(root) | |
if (err != nil) { | |
return walkFn(root, nil, err) | |
} | |
return walk(root, info, walkFn, recursive) | |
} | |
func walk(path string, info os.FileInfo, walkFn filepath.WalkFunc, recursive bool) error { | |
err := walkFn(path, info, nil) | |
if err != nil { | |
if info.IsDir() { | |
return nil | |
} | |
return err | |
} | |
if !info.IsDir() { | |
return nil | |
} | |
list, err := ioutil.ReadDir(path) | |
if err != nil { | |
return walkFn(path, info, err) | |
} | |
for _, fileInfo := range list { | |
if recursive { | |
err = walk(filepath.Join(path, fileInfo.Name()), fileInfo, walkFn, recursive) | |
if err != nil { | |
if !fileInfo.IsDir() { | |
return err | |
} | |
} | |
} else { | |
walkFn(filepath.Join(path, fileInfo.Name()), fileInfo, nil) | |
} | |
} | |
return nil | |
} | |
// http://stackoverflow.com/a/9561388 | |
func AppendIfMissing(slice []string, i string) []string { | |
for _, ele := range slice { | |
if ele == i { | |
return slice | |
} | |
} | |
return append(slice, i) | |
} | |
// borrowed from github.com/gorilla/mux | |
// matchInArray returns true if the given string value is in the array. | |
func matchInArray(arr []string, value string) bool { | |
for _, v := range arr { | |
if v == value { | |
return true | |
} | |
} | |
return false | |
} | |
func TestPath(s string) bool { | |
fi,err := os.Stat(s) | |
switch { | |
case os.IsNotExist(err): | |
return false | |
case err != nil: | |
return false | |
case !fi.IsDir(): | |
return false | |
} | |
return true | |
} | |
// simple round to closest integer | |
// https://groups.google.com/d/msg/golang-nuts/ITZV08gAugI/N01TlFI_bU8J | |
func Round(value float64) int { | |
if value < 0.0 { | |
value -= 0.5 | |
} else { | |
value += 0.5 | |
} | |
return int(value) | |
} | |
func ResizeImage(imgPath, imgName, dstPath string) error { | |
imgFile, err := os.Open(imgPath) | |
if err != nil { | |
return err | |
} | |
defer imgFile.Close() | |
// pastikan nilai maxWidth dan maxHeight bukan <= 0 | |
mw := Round(math.Max(*flagMaxWidth, 1)) | |
mh := Round(math.Max(*flagMaxHeight, 1)) | |
dst, _, err := images.Decode(imgFile, &images.DecodeOpts{MaxWidth: mw, MaxHeight: mh}) | |
if err != nil { | |
return err | |
} | |
b := dst.Bounds() | |
newExt := "_" + strconv.Itoa(b.Dx()) + "x" + strconv.Itoa(b.Dy()) + "." + *flagOutputFormat | |
newName := strings.Replace(imgName, filepath.Ext(imgName), newExt, -1) | |
newImg, err := os.Create(filepath.Join(dstPath, newName)) | |
if err != nil { | |
return err | |
} | |
defer newImg.Close() | |
switch { | |
case *flagOutputFormat == "jpeg": | |
if err := jpeg.Encode(newImg, dst, nil); err != nil { | |
fmt.Println(err) | |
} | |
case *flagOutputFormat == "png": | |
if err := png.Encode(newImg, dst); err != nil { | |
fmt.Println(err) | |
} | |
} | |
return nil | |
} | |
func UseImagemagick(imgPath, imgName, dstPath string) error { | |
var pixel string | |
mw := strconv.FormatFloat(*flagMaxWidth, 'f', 2, 64) | |
mh := strconv.FormatFloat(*flagMaxHeight, 'f', 2, 64) | |
switch { | |
case *flagMaxWidth == 0: | |
pixel = "x" + mh //strconv.Itoa(int(*flagMaxHeight)) | |
case *flagMaxHeight == 0: | |
pixel = mw //strconv.Itoa(int(*flagMaxWidth)) | |
default: | |
//pixel = strconv.Itoa(*flagMaxWidth) + "x" + strconv.Itoa(*flagMaxHeight) | |
pixel = mw + "x" + mh | |
} | |
newExt := "_%[filename:mysize]." + *flagOutputFormat | |
newName := strings.Replace(imgName, filepath.Ext(imgName), newExt, -1) | |
convertParams := "-filter Lanczos -sampling-factor 1x1 -unsharp 1.5x1+0.7+0.02 -quality 90" | |
if *flagCustomIM != "" { | |
convertParams = *flagCustomIM | |
} | |
// convert -resize 1024x768 -filter Lanczos -sampling-factor 1x1 -unsharp 1.5x1+0.7+0.02 -quality 90 imgPath -set filename:mysize "%wx%h" filepath.Join(filepath.Dir(imgPath), newName) | |
cArgs := []string{"-resize", pixel, imgPath, "-set", "filename:mysize", "%wx%h", "-auto-orient"}//, filepath.Join(dstPath, newName)} | |
cArgs = append(cArgs, strings.Fields(convertParams)...) | |
cArgs = append(cArgs, filepath.Join(dstPath, newName)) | |
//fmt.Println(cArgs) | |
err := exec.Command("convert", cArgs...).Run() | |
if err != nil { | |
return err | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment