<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【GoCN酷Go推薦】Golang 圖片處理 — image 庫

          共 7994字,需瀏覽 16分鐘

           ·

          2021-07-04 12:23

          在開發(fā)中,有時(shí)會(huì)遇到對(duì)圖片的處理需求,在 Python中, PIL/Pillow  庫非常強(qiáng)大和易用。

          而 Golang 語言中,處理圖片的標(biāo)準(zhǔn)庫 image也可以實(shí)現(xiàn)一些基本操作。

          image 庫支持常見的 PNG、JPEG、GIF 等格式的圖片處理, 可以對(duì)圖片進(jìn)行讀取、裁剪、繪制、生成等操作。

          讀取、新建圖片

          讀取

          圖片的讀取,和文件的讀取類似,只需要使用 os.Open()函數(shù),獲取一個(gè)輸入流,然后將數(shù)據(jù)流進(jìn)行解碼操作。

          需要注意的是,在解碼階段,要將不同類型的圖片的解碼器先進(jìn)行注冊(cè),這樣才不會(huì)報(bào)unknown format 的錯(cuò)誤。

          package main

          import (
           "fmt"
           "image"
           _ "image/png"
           "os"
          )

          func main() {
           f, err := os.Open("./gopher.png")
           if err != nil {
            panic(err)
           }
           img, formatName, err := image.Decode(f)
           if err != nil {
            panic(err)
           }
           fmt.Println(formatName)
           fmt.Println(img.Bounds())
           fmt.Println(img.ColorModel())
          }

          Decode 方法返回的第一個(gè)值是一個(gè) image.Image類型接口。不同的顏色模型的圖片返回不同類型的值。該接口有三個(gè)方法:

          type Image interface {
            ColorModel() color.Model // 返回圖片的顏色模型
            Bounds() Rectangle     // 返回圖片的長寬
            At(x, y int) color.Color // 返回(x,y)像素點(diǎn)的顏色
          }

          image 庫中很多結(jié)構(gòu)都實(shí)現(xiàn)了該接口,對(duì)于一些標(biāo)準(zhǔn)庫中沒有實(shí)現(xiàn)的功能,我們也可以自己實(shí)現(xiàn)該接口去滿足。

          新建

          如果是需要新建一個(gè)圖片,可以使用image.NewRGBA()方法。

          img := image.NewRGBA(image.Rect(00300300))

          這里的 NewRGBA方法返回的是一個(gè)實(shí)現(xiàn)了 image.Image接口的 image.RGBA類型數(shù)據(jù)。這里是一個(gè)300*300的透明背景的圖片。

          保存圖片

          保存圖片和保存文件也類似,需要先將圖片編碼,然后以數(shù)據(jù)流的形式寫入文件。

          img := image.NewRGBA(image.Rect(00300300))

          outFile, err := os.Create("gopher2.png")
          defer outFile.Close()
          if err != nil {
            panic(err)
          }
          b := bufio.NewWriter(outFile)
          err = png.Encode(b, img)
          if err != nil {
            panic(err)
          }
          err = b.Flush()
          if err != nil {
            panic(err)
          }

          圖片

          圖片的裁剪主要使用SubImage()方法,如下:

          img := image.NewRGBA(image.Rect(00300300))
          subImage := img.SubImage(image.Rect(002020))

          該方法將從創(chuàng)建的300 * 300的圖片裁剪出20 * 20 像素的子圖片。

          繪制圖片

          繪制圖片主要使用到 draw.Drawdraw.DrawMask方法。

          func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)

          func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)

          Draw

          Draw方法各個(gè)參數(shù)含義如下:

          • dst  繪圖的背景圖
          • r  背景圖的繪圖區(qū)域
          • src  要繪制的圖
          • sp  src 對(duì)應(yīng)的繪圖開始點(diǎn)
          • op 組合方式

          以下代碼是將一個(gè) Gopher 的圖案繪制到了一張黑色背景空白圖的左上角。

          f, err := os.Open("./gopher.png")
          if err != nil {
            panic(err)
          }
          gopherImg, _, err := image.Decode(f) // 打開圖片

          img := image.NewRGBA(image.Rect(00300300))
          for x := 0; x < img.Bounds().Dx(); x++ {    // 將背景圖涂黑
            for y := 0; y < img.Bounds().Dy(); y++ {
              img.Set(x, y, color.Black)
            }
          }
          draw.Draw(img, img.Bounds(), gopherImg, image.Pt(00), draw.Over) // 將gopherImg繪制到背景圖上

          DrawMask

          DrawMask方法多了一個(gè)遮罩蒙層參數(shù)mask,以及蒙層的起始位置參數(shù) mp

          Draw方法是 DrawMask的一種特殊形式,當(dāng) DrawMaskmask 參數(shù)為nil時(shí),即為Draw方法。

          DrawMask將背景圖上的繪圖區(qū)域起始點(diǎn)、要繪制圖的起始點(diǎn)、遮罩蒙層的起始點(diǎn)進(jìn)行對(duì)齊,然后對(duì)背景圖上的繪圖矩陣區(qū)域執(zhí)行 Porter-Duff合并操作。

          下面是給圖片加一個(gè)圓形遮罩的示例:

          func drawCirclePic() {
           f, err := os.Open("./gopher.png")
           if err != nil {
            panic(err)
           }
           gopherImg, _, err := image.Decode(f)


           d := gopherImg.Bounds().Dx()
           
           //將一個(gè)cicle作為蒙層遮罩,圓心為圖案中點(diǎn),半徑為邊長的一半
           c := circle{p: image.Point{X: d / 2, Y: d / 2}, r: d / 2
           circleImg := image.NewRGBA(image.Rect(00, d, d))
           draw.DrawMask(circleImg, circleImg.Bounds(), gopherImg, image.Point{}, &c, image.Point{}, draw.Over)

           SavePng(circleImg)
          }

          type circle struct { // 這里需要自己實(shí)現(xiàn)一個(gè)圓形遮罩,實(shí)現(xiàn)接口里的三個(gè)方法
           p image.Point // 圓心位置
           r int
          }

          func (c *circle) ColorModel() color.Model {
           return color.AlphaModel
          }

          func (c *circle) Bounds() image.Rectangle {
           return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
          }

          // 對(duì)每個(gè)像素點(diǎn)進(jìn)行色值設(shè)置,在半徑以內(nèi)的圖案設(shè)成完全不透明
          func (c *circle) At(x, y int) color.Color {
           xx, yy, rr := float64(x-c.p.X)+0.5float64(y-c.p.Y)+0.5float64(c.r)
           if xx*xx+yy*yy < rr*rr {
            return color.Alpha{A: 255}
           }
           return color.Alpha{}
          }

          給圖片加一個(gè)圓角遮罩的示例:

          func drawRadiusPic() {
           f, err := os.Open("./gopher.png")
           if err != nil {
            panic(err)
           }
           gopherImg, _, err := image.Decode(f)

           w := gopherImg.Bounds().Dx()
           h := gopherImg.Bounds().Dy()

           c := radius{p: image.Point{X: w, Y: h}, r: int(40)}
           radiusImg := image.NewRGBA(image.Rect(00, w, h))
           draw.DrawMask(radiusImg, radiusImg.Bounds(), gopherImg, image.Point{}, &c, image.Point{}, draw.Over)

           SavePng(radiusImg)
          }

          type radius struct {
           p image.Point // 矩形右下角位置
           r int
          }

          func (c *radius) ColorModel() color.Model {
           return color.AlphaModel
          }

          func (c *radius) Bounds() image.Rectangle {
           return image.Rect(00, c.p.X, c.p.Y)
          }

          // 對(duì)每個(gè)像素點(diǎn)進(jìn)行色值設(shè)置,分別處理矩形的四個(gè)角,在四個(gè)角的內(nèi)切圓的外側(cè),色值設(shè)置為全透明,其他區(qū)域不透明
          func (c *radius) At(x, y int) color.Color {
           var xx, yy, rr float64
           var inArea bool
           // left up
           if x <= c.r && y <= c.r {
            xx, yy, rr = float64(c.r-x)+0.5float64(y-c.r)+0.5float64(c.r)
            inArea = true
           }
           // right up
           if x >= (c.p.X-c.r) && y <= c.r {
            xx, yy, rr = float64(x-(c.p.X-c.r))+0.5float64(y-c.r)+0.5float64(c.r)
            inArea = true
           }
           // left bottom
           if x <= c.r && y >= (c.p.Y-c.r) {
            xx, yy, rr = float64(c.r-x)+0.5float64(y-(c.p.Y-c.r))+0.5float64(c.r)
            inArea = true
           }
           // right bottom
           if x >= (c.p.X-c.r) && y >= (c.p.Y-c.r) {
            xx, yy, rr = float64(x-(c.p.X-c.r))+0.5float64(y-(c.p.Y-c.r))+0.5float64(c.r)
            inArea = true
           }

           if inArea && xx*xx+yy*yy >= rr*rr {
            return color.Alpha{}
           }
           return color.Alpha{A: 255}
          }

          在圖案進(jìn)行圓形、圓角繪制的過程中,因?yàn)樽钚挝皇?px,所以可能會(huì)有鋸齒邊緣的問題,解決這個(gè)問題可以通過先將原圖放大,遮罩后再縮小來解決。

          Reference

          The Go image/draw package - The Go Blog (golang.org)https://blog.golang.org/image-draw)

          Porter-Duff blend 模式 - Xamarin | Microsoft Docs(https://docs.microsoft.com/zh-tw/xamarin/xamarin-forms/user-interface/graphics/skiasharp/effects/blend-modes/porter-duff)


          還想了解更多嗎?

          歡迎加入我們GOLANG中國社區(qū):https://gocn.vip/


          《酷Go推薦》招募:


          各位Gopher同學(xué),最近我們社區(qū)打算推出一個(gè)類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個(gè)庫或者好的項(xiàng)目,然后寫一點(diǎn)這個(gè)庫使用方法或者優(yōu)點(diǎn)之類的,這樣可以真正的幫助到大家能夠?qū)W習(xí)到

          新的庫,并且知道怎么用。


          大概規(guī)則和每日新聞?lì)愃疲绻麍?bào)名人多的話每個(gè)人一個(gè)月輪到一次,歡迎大家報(bào)名!(報(bào)名地址:https://wj.qq.com/s2/7734329/3f51)


          掃碼也可以加入 GoCN 的大家族喲~


          瀏覽 170
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼1234 | 79色热在线 | 国产一级特黄AAAAA片一 国产一级婬片A片免费看狼牙 | 精品久久精品 | 成人黄色免费在线视频 |