Skip to content

Instantly share code, notes, and snippets.

@linw1995
Last active February 8, 2023 06:48
Show Gist options
  • Save linw1995/41e103f29e7dd97679c577a6f4830be8 to your computer and use it in GitHub Desktop.
Save linw1995/41e103f29e7dd97679c577a6f4830be8 to your computer and use it in GitHub Desktop.
Using golang to mark the picture
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)
}
}
}
@linw1995
Copy link
Author

linw1995 commented Jun 25, 2017

1
1_marked

@arikaa
Copy link

arikaa commented Aug 23, 2018

Hello, the packages you have included didn't work for me and I had to use:

"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/vgimg"

@zeel-wu
Copy link

zeel-wu commented Jun 7, 2022

你好,现在这个包没有此方法了,请问是哪个?这是报错undefined: vg.MakeFont

@zhangbo
Copy link

zhangbo commented Jun 9, 2022

我使用的是0.11的plot
import (
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/font/liberation"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/vgimg"
)

WaterMark方法修改如下, 从第7行开始:

// 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
fontStyle, _ := vg.MakeFont("Courier", vg.Inch)
var cache = font.NewCache(liberation.Collection())
var ft = cache.Lookup(font.Font{Typeface: "Courier", Variant: "Sans"}, vg.Inch)

// repeat the markText
markTextWidth := fontStyle.Width(markText)
unitText := markText
for markTextWidth <= 1.5*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.2
for offset := -2 * diagonal; offset < 2*diagonal; offset += lineHeight {
	c.FillString(ft, 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

@zeel-wu
Copy link

zeel-wu commented Jun 10, 2022

亲测有效
"gonum.org/v1/plot/vg" 用v0.8.1
增加支持中文字体,字体文件为PingFang SC Medium.ttf

package main

import (
  "bytes"
  "flag"
  "fmt"
  "github.com/golang/freetype/truetype"
  "strings"

  "io/ioutil"
  "os"
  "path"

  "math"
  "math/rand"
  "time"

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

  "gonum.org/v1/plot/vg"
  "gonum.org/v1/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) {
  // custom font
  var fontName = flag.String("font", "Courier", "Name of the font to use")
  ttfBytes, err := ioutil.ReadFile("PingFang SC Medium.ttf")
  fi, err := truetype.Parse(ttfBytes)
  vg.AddFont(*fontName, fi)
  //f, err := freetype.ParseFont(ttfBytes)
  //vg.AddFont(*fontName, f)

  // 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)
  fontStyle, _ := vg.MakeFont(*fontName, vg.Inch*0.4)

  // 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
  //r := math.Pi * rand.Float64() / 2
  //c.Rotate(r)

  // 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)
  //}
  c.FillString(fontStyle, vg.Point{X: 200, Y: 400}, 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)
    }
  }
}

@ABetterCat
Copy link

您好,请问不透明度怎么调

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment