用golang给图片加水印

2017-06-25  本文已影响1172人  linw1995

前言

大前天上班的时候,坐在旁边的同事需要一批有水印的图片来测试模型。然后我就主动站出来……
他问我怎么加,我说用golang可以。可为啥我不用更容易实现的Python呢……可能是为了装逼。

代码

WaterMark.go | Gist

package main

import (
    "bytes"
    "fmt"
    "strings"

    "io/ioutil"
    "os"
    "path"

    "math"
    "math/rand"
    "time"

    "image"
    "image/color"
    "image/draw"
    "image/jpeg"
    "image/png"

    "github.com/gonum/plot/vg"
    "github.com/gonum/plot/vg/vgimg"
)

func init() {
    rand.Seed(time.Now().UnixNano())
}

// WaterMark for adding a watermark on the image
func WaterMark(img image.Image, markText string) (image.Image, error) {
    // image's length to canvas's length
    bounds := img.Bounds()
    w := vg.Length(bounds.Max.X) * vg.Inch / vgimg.DefaultDPI
    h := vg.Length(bounds.Max.Y) * vg.Inch / vgimg.DefaultDPI
    diagonal := vg.Length(math.Sqrt(float64(w*w + h*h)))

    // create a canvas, which width and height are diagonal
    c := vgimg.New(diagonal, diagonal)

    // draw image on the center of canvas
    rect := vg.Rectangle{}
    rect.Min.X = diagonal/2 - w/2
    rect.Min.Y = diagonal/2 - h/2
    rect.Max.X = diagonal/2 + w/2
    rect.Max.Y = diagonal/2 + h/2
    c.DrawImage(rect, img)

    // make a fontStyle, which width is vg.Inch * 0.7
    fontStyle, _ := vg.MakeFont("Courier", vg.Inch*0.7)

    // repeat the markText
    markTextWidth := fontStyle.Width(markText)
    unitText := markText
    for markTextWidth <= diagonal {
        markText += " " + unitText
        markTextWidth = fontStyle.Width(markText)
    }

    // set the color of markText
    c.SetColor(color.RGBA{0, 0, 0, 122})

    // set a random angle between 0 and π/2
    θ := math.Pi * rand.Float64() / 2
    c.Rotate(θ)

    // set the lineHeight and add the markText
    lineHeight := fontStyle.Extents().Height * 1
    for offset := -2 * diagonal; offset < 2*diagonal; offset += lineHeight {
        c.FillString(fontStyle, vg.Point{X: 0, Y: offset}, markText)
    }

    // canvas writeto jpeg
    // canvas.img is private
    // so use a buffer to transfer
    jc := vgimg.PngCanvas{Canvas: c}
    buff := new(bytes.Buffer)
    jc.WriteTo(buff)
    img, _, err := image.Decode(buff)
    if err != nil {
        return nil, err
    }

    // get the center point of the image
    ctp := int(diagonal * vgimg.DefaultDPI / vg.Inch / 2)

    // cutout the marked image
    size := bounds.Size()
    bounds = image.Rect(ctp-size.X/2, ctp-size.Y/2, ctp+size.X/2, ctp+size.Y/2)
    rv := image.NewRGBA(bounds)
    draw.Draw(rv, bounds, img, bounds.Min, draw.Src)
    return rv, nil
}

// MarkingPicture for marking picture with text
func MarkingPicture(filepath, text string) (image.Image, error) {
    f, err := os.Open(filepath)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    img, _, err := image.Decode(f)
    if err != nil {
        return nil, err
    }

    img, err = WaterMark(img, text)
    if err != nil {
        return nil, err
    }
    return img, nil
}

func writeTo(img image.Image, ext string) (rv *bytes.Buffer, err error) {
    ext = strings.ToLower(ext)
    rv = new(bytes.Buffer)
    switch ext {
    case ".jpg", ".jpeg":
        err = jpeg.Encode(rv, img, &jpeg.Options{Quality: 100})
    case ".png":
        err = png.Encode(rv, img)
    }
    return rv, err
}

func main() {
    if len(os.Args) < 3 {
        panic(fmt.Errorf("Please using like this:$ %s [file or directory] [text]", os.Args[0]))
    }

    target := os.Args[1]
    text := os.Args[2]

    if stat, err := os.Stat(target); err == nil && stat.IsDir() {
        files, _ := ioutil.ReadDir(target)
        for _, fn := range files {
            img, err := MarkingPicture(path.Join(target, fn.Name()), text)
            if err != nil {
                continue
            }

            ext := path.Ext(fn.Name())
            base := strings.Split(fn.Name(), ".")[0] + "_marked"
            f, err := os.Create(base + ext)
            if err != nil {
                panic(err)
            }

            buff, err := writeTo(img, ext)
            if err != nil {
                panic(err)
            }
            if _, err = buff.WriteTo(f); err != nil {
                panic(err)
            }
        }
    } else {
        img, err := MarkingPicture(target, text)
        if err != nil {
            panic(err)
        }

        ext := path.Ext(target)
        base := strings.Split(path.Base(target), ".")[0] + "_marked"
        f, err := os.Create(base + ext)
        if err != nil {
            panic(err)
        }

        buff, err := writeTo(img, ext)
        if err != nil {
            panic(err)
        }

        if _, err = buff.WriteTo(f); err != nil {
            panic(err)
        }
    }
}

图片效果

用golang给图片加水印

原文链接:用golang给图片打水印 | linw1995

上一篇 下一篇

猜你喜欢

热点阅读