最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go實現(xiàn)圖片上添加水印的示例代碼

 更新時間:2025年05月25日 09:17:21   作者:尋找09之夏  
本文主要介紹了Go實現(xiàn)圖片上添加水印的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

在數(shù)字內容日益重要的今天,保護版權和標識來源變得關鍵。為圖片添加水印有助于聲明所有權、提升品牌認知度,并防止未經(jīng)授權的使用。本文將介紹如何用Go語言實現(xiàn)圖片水印,包括靜態(tài)圖片和帶旋轉、傾斜效果的文字水印,幫助您有效保護數(shù)字內容。我們將逐步解析關鍵步驟,確保清晰易懂。

一、準備工作

為了順利實現(xiàn)圖片水印功能,您需要完成以下幾個準備步驟:

1.安裝Go語言環(huán)境:確保您的開發(fā)環(huán)境中已經(jīng)安裝了Go語言,并具備基本的Go編程知識。

2.安裝必要的庫

  • golang.org/x/image/draw:支持高質量縮放及其他圖像繪制操作。
  • github.com/disintegration/imaging:提供簡便的API用于圖像變換,如旋轉和傾斜。

3.準備圖像資源

  • 主圖 (Base Image):這是您想要添加水印的原始圖像。它可以是任何您有權處理的圖像文件。
  • 水印圖 (Watermark Image):這是將被放置在主圖之上的圖像,通常是一個透明背景的PNG文件,這樣可以確保它不會遮擋主圖的重要細節(jié)。

確保您擁有上述所有工具和資源后,就可以開始編寫代碼來實現(xiàn)圖片水印功能了。接下來的章節(jié)將逐步指導您如何加載主圖、應用水印圖并保存最終結果。

二、圖片加水印

2.1 圖片水印

2.1.1 打開主圖

首先,我們需要打開并讀取主圖文件。這一步確保了程序能夠訪問到用戶想要處理的原始圖像。

// 打開主圖文件
mainImageFile, err := os.Open("main.png")
if err != nil {
	log.Fatalf("Failed to open main image: %v", err)
}
defer mainImageFile.Close()

2.1.2 解碼主圖

接下來,從輸入流中讀取原始圖像并解碼它。如果解碼過程中出現(xiàn)問題,程序將返回錯誤信息。這里我們使用image.Decode函數(shù)自動識別圖像格式。

mainImageFile, err := os.Open("main.png")
if err != nil {
	log.Fatalf("Failed to open main image: %v", err)
}
defer mainImageFile.Close()

2.1.3 打開水印圖片

然后,我們需要打開水印圖片文件。與主圖類似,我們也需要確保能夠正確讀取和解碼水印圖像。

// 打開水印圖片
watermarkImageFile, err := os.Open("logo.png") // 可以替換為其他圖片文件名
if err != nil {
	log.Fatalf("Failed to open watermark image: %v", err)
}
defer watermarkImageFile.Close()

2.1.4 解碼水印圖片

接下來,從輸入流中讀取水印圖像并解碼它。如果解碼過程中出現(xiàn)問題,程序將返回錯誤信息。這里我們再次使用image.Decode函數(shù)自動識別圖像格式。

// 解碼水印
watermarkImage, _, err := image.Decode(watermarkImageFile)
if err != nil {
	log.Fatalf("Failed to decode watermark image: %v", err)
}

2.1.5 計算縮放比例

為了保證水印不會過于顯眼或遮擋過多內容,根據(jù)原始圖像的尺寸計算水印的最大寬度和高度。通常,我們會設定最大值為原始圖像寬高的25%。然后基于這些最大值計算出適當?shù)目s放比例。

// 獲取主圖和水印的邊界矩形
mainImageBounds := mainImage.Bounds()
watermarkImageBounds := watermarkImage.Bounds()

// 計算水印的最大尺寸
maxWatermarkWidth := int(float64(mainImageBounds.Max.X) * 0.25)  // 最大寬度為主圖寬度的25%
maxWatermarkHeight := int(float64(mainImageBounds.Max.Y) * 0.25) // 最大高度為主圖高度的25%

// 計算水印的縮放比例
scale := 1.0
if watermarkImageBounds.Max.X > maxWatermarkWidth || watermarkImageBounds.Max.Y > maxWatermarkHeight {
	scale = math.Min(
		float64(maxWatermarkWidth)/float64(watermarkImageBounds.Max.X),
		float64(maxWatermarkHeight)/float64(watermarkImageBounds.Max.Y),
	)
}

// 應用縮放比例
watermarkWidth := int(float64(watermarkImageBounds.Max.X) * scale)
watermarkHeight := int(float64(watermarkImageBounds.Max.Y) * scale)

2.1.6 創(chuàng)建新的圖像

創(chuàng)建一個新的RGBA圖像,其大小與原始圖像相同,并將原始圖像復制到這個新圖像中。

// 創(chuàng)建一個新的圖像,大小與主圖相同
resultImage := image.NewRGBA(mainImageBounds)

// 將主圖復制到新圖像中
draw.Draw(resultImage, mainImageBounds, mainImage, mainImageBounds.Min, draw.Src)

2.1.7 縮放水印圖像

根據(jù)前面計算的縮放比例調整水印圖像的大小。我們可以使用golang.org/x/image/draw包中的draw.CatmullRom.Scale方法來進行高質量縮放。

// 創(chuàng)建一個用于存放縮放后水印的新圖像
resizedWatermarkImage := image.NewRGBA(image.Rect(0, 0, watermarkWidth, watermarkHeight))

// 使用高質量縮放算法縮放水印圖像
draw.CatmullRom.Scale(resizedWatermarkImage, resizedWatermarkImage.Bounds(), watermarkImage, watermarkImageBounds, draw.Over, nil)

2.1.8 確定水印位置

根據(jù)用戶提供的參數(shù)確定水印應該放置的位置,例如左上角、右上角等。對于每個預設的位置,我們計算出相應的坐標點。這里僅給出右下角的例子:

// 引入 position 變量,并賦值為一個有效的水印位置常量
position := "left_top" // 假設使用 "left_top" 作為示例

// 計算水印放置的位置
var watermarkX, watermarkY int
switch position {
case "left_top":
	watermarkX = int(float64(mainImageBounds.Max.X) * 0.02) // 2% of the width
	watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 2% of the height
case "right_top":
	watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 98% of the width minus watermark width
	watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02)                // 2% of the height
case "left_bottom":
	watermarkX = int(float64(mainImageBounds.Max.X) * 0.02)                 // 2% of the width
	watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 98% of the height minus watermark height
case "right_bottom":
	watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth  // 98% of the width minus watermark width
	watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 98% of the height minus watermark height
default:
	log.Fatalf("Invalid watermark position: %v", position)
}

2.1.9 繪制水印

最后,使用draw.Draw方法將調整后的水印繪制到新圖像的指定位置。

// 將水印繪制到新圖像的指定位置
draw.Draw(resultImage, image.Rectangle{
	Min: image.Point{X: watermarkX, Y: watermarkY},
	Max: image.Point{X: watermarkX + watermarkWidth, Y: watermarkY + watermarkHeight},
}, resizedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over)

2.1.10 繪制旋轉水?。蛇x)

為了讓水印更加多樣化,可以引入旋轉或傾斜的效果。這可以通過創(chuàng)建一個仿射變換矩陣并應用于文字圖像來完成。以下是實現(xiàn)旋轉功能的代碼片段:

// 創(chuàng)建一個新的圖像,大小與水印相同
rotatedWatermarkImage := image.NewRGBA(resizedWatermarkImage.Bounds())

// 引入 rotation 變量,并賦值為一個有效的旋轉角度(度數(shù))
rotation := 45.0 // 假設使用 45.0 度作為示例

// 計算旋轉角度的弧度
radians := rotation * math.Pi / 180.0

// 計算旋轉后的中心點
centerX := float64(watermarkWidth) / 2.0
centerY := float64(watermarkHeight) / 2.0

// 遍歷每個像素點并應用旋轉
for y := 0; y < watermarkHeight; y++ {
	for x := 0; x < watermarkWidth; x++ {
		// 將像素點轉換為相對于中心點的坐標
		relX := float64(x) - centerX
		relY := float64(y) - centerY

		// 應用旋轉矩陣
		newX := relX*math.Cos(radians) - relY*math.Sin(radians)
		newY := relX*math.Sin(radians) + relY*math.Cos(radians)

		// 將旋轉后的坐標轉換回圖像坐標
		newX += centerX
		newY += centerY

		// 如果旋轉后的坐標在圖像范圍內,則繪制像素
		if newX >= 0 && newX < float64(watermarkWidth) && newY >= 0 && newY < float64(watermarkHeight) {
			rotatedWatermarkImage.Set(int(newX), int(newY), resizedWatermarkImage.At(x, y))
		}
	}
}

// 將旋轉后的水印繪制到新圖像的指定位置
draw.Draw(resultImage, image.Rectangle{Min: image.Point{X: watermarkX, Y: watermarkY}, Max: image.Point{X: watermarkX + watermarkWidth, Y: watermarkY + watermarkHeight}}, rotatedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over)

2.1.11 保存結果圖像

根據(jù)原始圖像的格式(如PNG或JPEG),將帶有水印的新圖像編碼并保存到內存中的緩沖區(qū),然后再寫入磁盤。

// 保存結果圖像到內存
var buffer bytes.Buffer
switch fileExtension {
case ".png":
	err = png.Encode(&buffer, resultImage)
case ".jpg", ".jpeg":
	err = jpeg.Encode(&buffer, resultImage, nil)
default:
	log.Fatalf("Unsupported file extension: %v", fileExtension)
}
if err != nil {
	log.Fatalf("Failed to encode image: %v", err)
}

// 保存結果圖像到文件
outputFileName := "output" + fileExtension
outputFile, err := os.Create(outputFileName)
if err != nil {
	log.Fatalf("Failed to create output file: %v", err)
}
defer outputFile.Close()

// 將內存中的圖像數(shù)據(jù)寫入文件
_, err = buffer.WriteTo(outputFile)
if err != nil {
	log.Fatalf("Failed to write to output file: %v", err)
}

2.1.12 完整代碼和效果

package main

import (
	"bytes"
	"golang.org/x/image/draw"
	"image"
	"image/jpeg"
	"image/png"
	"log"
	"os"
	"path/filepath"
)

func main() {
	// 打開主圖文件
	mainImageFile, err := os.Open("main.png")
	if err != nil {
		log.Fatalf("Failed to open main image: %v", err)
	}
	defer mainImageFile.Close()

	// 獲取文件擴展名
	fileExtension := filepath.Ext(mainImageFile.Name())

	// 解碼主圖
	mainImage, _, err := image.Decode(mainImageFile)
	if err != nil {
		log.Fatalf("Failed to decode main image: %v", err)
	}

	// 打開水印圖片
	watermarkImageFile, err := os.Open("logo.png") // 你可以將 "logo.png" 替換為 "logo.jpg" 或其他圖片文件名
	if err != nil {
		log.Fatalf("Failed to open watermark image: %v", err)
	}
	defer watermarkImageFile.Close()

	// 解碼水印
	watermarkImage, _, err := image.Decode(watermarkImageFile)
	if err != nil {
		log.Fatalf("Failed to decode watermark image: %v", err)
	}

	// 獲取主圖和水印的邊界矩形
	mainImageBounds := mainImage.Bounds()
	watermarkImageBounds := watermarkImage.Bounds()

	// 計算水印的最大尺寸
	maxWatermarkWidth := int(float64(mainImageBounds.Max.X) * 0.20)  // 你可以將 "0.20" 替換為 "0.15" 或其他值
	maxWatermarkHeight := int(float64(mainImageBounds.Max.Y) * 0.20) // 你可以將 "0.20" 替換為 "0.15" 或其他值

	// 計算水印的縮放比例
	watermarkWidth := watermarkImageBounds.Max.X
	watermarkHeight := watermarkImageBounds.Max.Y

	// 計算縮放比例
	scale := 1.0
	if watermarkWidth > maxWatermarkWidth {
		scale = float64(maxWatermarkWidth) / float64(watermarkWidth)
	}
	if watermarkHeight > maxWatermarkHeight {
		if scale > float64(maxWatermarkHeight)/float64(watermarkHeight) {
			scale = float64(maxWatermarkHeight) / float64(watermarkHeight)
		}
	}

	// 應用縮放比例
	watermarkWidth = int(float64(watermarkWidth) * scale)
	watermarkHeight = int(float64(watermarkHeight) * scale)

	// 創(chuàng)建一個新的圖像,大小與主圖相同
	resultImage := image.NewRGBA(mainImageBounds)

	// 將主圖復制到新圖像中
	draw.Draw(resultImage, mainImageBounds, mainImage, mainImageBounds.Min, draw.Src)

	// 縮放水印圖像
	resizedWatermarkImage := image.NewRGBA(image.Rect(0, 0, watermarkWidth, watermarkHeight))
	draw.NearestNeighbor.Scale(resizedWatermarkImage, resizedWatermarkImage.Bounds(), watermarkImage, watermarkImageBounds, draw.Over, nil)

	// 引入 position 變量,并賦值為一個有效的水印位置常量
	position := "left_top" // 假設使用 "left_top" 作為示例

	// 計算水印放置的位置
	var watermarkX, watermarkY int
	switch position {
	case "left_top":
		watermarkX = int(float64(mainImageBounds.Max.X) * 0.02) // 寬度的2%
		watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02) // 高度的2%
	case "right_top":
		watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth // 寬度的98%減去水印寬度
		watermarkY = int(float64(mainImageBounds.Max.Y) * 0.02)                // 高度的2%
	case "left_bottom":
		watermarkX = int(float64(mainImageBounds.Max.X) * 0.02)                 // 寬度的2%
		watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 高度的98%減去水印高度
	case "right_bottom":
		watermarkX = int(float64(mainImageBounds.Max.X)*0.98) - watermarkWidth  // 寬度的98%減去水印寬度
		watermarkY = int(float64(mainImageBounds.Max.Y)*0.98) - watermarkHeight // 高度的98%減去水印高度
	default:
		log.Fatalf("Invalid watermark position: %v", position)
	}

	// 將水印繪制到新圖像的指定位置
	draw.Draw(resultImage, image.Rectangle{Min: image.Point{X: watermarkX, Y: watermarkY}, Max: image.Point{X: watermarkX + watermarkWidth, Y: watermarkY + watermarkHeight}}, resizedWatermarkImage, image.Point{X: 0, Y: 0}, draw.Over)

	// 保存結果圖像到內存
	var buffer bytes.Buffer
	switch fileExtension {
	case ".png":
		err = png.Encode(&buffer, resultImage)
	case ".jpg", ".jpeg":
		err = jpeg.Encode(&buffer, resultImage, nil)
	default:
		log.Fatalf("Unsupported file extension: %v", fileExtension)
	}
	if err != nil {
		log.Fatalf("Failed to encode image: %v", err)
	}

	// 保存結果圖像到文件
	outputFile, err := os.Create("output" + fileExtension)
	if err != nil {
		log.Fatalf("Failed to create output file: %v", err)
	}
	defer outputFile.Close() // 添加文件關閉操作

	// 將內存中的圖像數(shù)據(jù)寫入文件
	_, err = buffer.WriteTo(outputFile)
	if err != nil {
		log.Fatalf("Failed to write to output file: %v", err)
	}
}

總結

通過以上步驟,我們不僅完成了在圖片上添加靜態(tài)圖片水印的功能實現(xiàn),還增加了旋轉、傾斜的水印功能,使得生成的水印更加多樣化和個性化。您可以根據(jù)自己的需求進一步優(yōu)化代碼,比如支持更多的水印位置選項,或者允許用戶上傳自定義水印圖片。希望這篇文章能幫助您理解和實現(xiàn)這一常見但非常有用的功能。如果您有任何問題或遇到困難,請隨時查閱相關文檔或尋求社區(qū)的幫助。

到此這篇關于Go實現(xiàn)圖片上添加水印的示例代碼的文章就介紹到這了,更多相關Go 圖片上添加水印內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • golang Git Hook自動化實現(xiàn)的示例代碼

    golang Git Hook自動化實現(xiàn)的示例代碼

    本文主要介紹了golang Git Hook自動化實現(xiàn)的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2026-04-04
  • golang類型推斷與隱式類型轉換

    golang類型推斷與隱式類型轉換

    這篇文章主要介紹了golang類型推斷與隱式類型轉換,golang類型推斷可以省略類型,像寫動態(tài)語言代碼一樣,讓編程變得更加簡單,同時也保留了靜態(tài)類型的安全性
    2022-06-06
  • gorm FirstOrCreate和受影響的行數(shù)實例

    gorm FirstOrCreate和受影響的行數(shù)實例

    這篇文章主要介紹了gorm FirstOrCreate和受影響的行數(shù)實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • golang語言http協(xié)議get拼接參數(shù)操作

    golang語言http協(xié)議get拼接參數(shù)操作

    這篇文章主要介紹了golang語言http協(xié)議get拼接參數(shù)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go1.16新特性embed打包靜態(tài)資源文件實現(xiàn)

    Go1.16新特性embed打包靜態(tài)資源文件實現(xiàn)

    這篇文章主要為大家介紹了Go?1.16新特性embed打包靜態(tài)資源文件的實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Go編譯原理之函數(shù)內聯(lián)

    Go編譯原理之函數(shù)內聯(lián)

    這篇文章主要為大家介紹了Go編譯原理之函數(shù)內聯(lián)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go語言中的原子操作使用詳解

    Go語言中的原子操作使用詳解

    這篇文章主要介紹了Go語言中的原子操作使用詳解的相關資料,需要的朋友可以參考下
    2023-08-08
  • 詳解Go語言中的逃逸分析

    詳解Go語言中的逃逸分析

    逃逸分析是編譯器用于決定將變量分配到棧上還是堆上的一種行為,下面小編就來為大家詳細講講go語言中是如何進行逃逸分析的,需要的小伙伴可以參考下
    2023-09-09
  • Go單元測試工具gomonkey的使用

    Go單元測試工具gomonkey的使用

    本文主要介紹了Go單元測試工具gomonkey的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • Go語言Telnet回音服務器的實現(xiàn)

    Go語言Telnet回音服務器的實現(xiàn)

    這篇文章主要介紹了Go語言Telnet回音服務器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01

最新評論

同德县| 徐闻县| 富民县| 七台河市| 平阳县| 江津市| 化隆| 丰原市| 开远市| 开化县| 西和县| 北辰区| 锡林郭勒盟| 岑溪市| 陇川县| 石嘴山市| 壶关县| 托里县| 泸西县| 黄大仙区| 玉龙| 游戏| 颍上县| 扶沟县| 阿拉善左旗| 祥云县| 汕头市| 长沙县| 葫芦岛市| 江都市| 融水| 成安县| 桑日县| 龙口市| 清河县| 布拖县| 乌兰察布市| 桃园市| 仪征市| 简阳市| 巴彦淖尔市|