【Go】:圖片上添加水印的全面指南——從基礎到高級特性

前言

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

一、準備工作

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

1.安裝Go語言環境:確保您的開發環境中已經安裝了Go語言,并具備基本的Go編程知識。

2.安裝必要的庫

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

3.準備圖像資源

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

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

二、圖片加水印

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 解碼主圖

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

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 解碼水印圖片

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

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

2.1.5 計算縮放比例

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

// 獲取主圖和水印的邊界矩形
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 創建新的圖像

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

// 創建一個新的圖像,大小與主圖相同
resultImage := image.NewRGBA(mainImageBounds)// 將主圖復制到新圖像中
draw.Draw(resultImage, mainImageBounds, mainImage, mainImageBounds.Min, draw.Src)

2.1.7 縮放水印圖像

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

// 創建一個用于存放縮放后水印的新圖像
resizedWatermarkImage := image.NewRGBA(image.Rect(0, 0, watermarkWidth, watermarkHeight))// 使用高質量縮放算法縮放水印圖像
draw.CatmullRom.Scale(resizedWatermarkImage, resizedWatermarkImage.Bounds(), watermarkImage, watermarkImageBounds, draw.Over, nil)

2.1.8 確定水印位置

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

// 引入 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 widthwatermarkY = 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 widthwatermarkY = 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 widthwatermarkY = 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 widthwatermarkY = 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 繪制旋轉水印(可選)

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

// 創建一個新的圖像,大小與水印相同
rotatedWatermarkImage := image.NewRGBA(resizedWatermarkImage.Bounds())// 引入 rotation 變量,并賦值為一個有效的旋轉角度(度數)
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) - centerXrelY := float64(y) - centerY// 應用旋轉矩陣newX := relX*math.Cos(radians) - relY*math.Sin(radians)newY := relX*math.Sin(radians) + relY*math.Cos(radians)// 將旋轉后的坐標轉換回圖像坐標newX += centerXnewY += 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 保存結果圖像

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

// 保存結果圖像到內存
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()// 將內存中的圖像數據寫入文件
_, err = buffer.WriteTo(outputFile)
if err != nil {log.Fatalf("Failed to write to output file: %v", err)
}

2.1.12 完整代碼和效果

package mainimport ("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.XwatermarkHeight := watermarkImageBounds.Max.Y// 計算縮放比例scale := 1.0if 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)// 創建一個新的圖像,大小與主圖相同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 intswitch 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.Bufferswitch 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() // 添加文件關閉操作// 將內存中的圖像數據寫入文件_, err = buffer.WriteTo(outputFile)if err != nil {log.Fatalf("Failed to write to output file: %v", err)}
}

2.2 文字水印

敬請期待!!!

總結

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65628.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65628.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65628.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

springCloudGateWay使用總結

1、什么是網關 功能: ①身份認證、權限驗證 ②服務器路由、負載均衡 ③請求限流 2、gateway搭建 2.1、創建一個空項目 2.2、引入依賴 2.3、加配置 3、斷言工廠 4、過濾工廠 5、全局過濾器 6、跨域問題

zig 安裝,Hello World 示例

1. 安裝 Zig 首先&#xff0c;你需要在你的計算機上安裝 Zig 編譯器。你可以從 Zig 官方網站 下載適合你操作系統的版本。 安裝完成后&#xff0c;你可以在終端中運行以下命令來檢查 Zig 是否安裝成功&#xff1a; zig version如果一切正常&#xff0c;它會顯示 Zig 的版本信…

【Docker】Docker與Docker compose離線安裝

文章目錄 一. 離線安裝1. 下載docker2. 安裝 二. 相關命令三. 配置docker-compose 一. 離線安裝 1. 下載docker wget https://download.docker.com/linux/static/stable/x86_64/docker-27.1.2.tgz wget https://download.docker.com/linux/static/stable/aarch64/docker-27.1…

【UE5 C++課程系列筆記】22——多線程基礎——FRunnable和FRunnableThread

目錄 1、FRunnable 1.1 概念 1.2 主要成員函數 &#xff08;1&#xff09;Init 函數 &#xff08;2&#xff09;Run 函數 &#xff08;3&#xff09;Stop 函數 &#xff08;4&#xff09;Exit 函數 2、FRunnableThread 2.1 概念 2.2 主要操作 &#xff08;1&#xff…

Vue重新加載子組件

背景&#xff1a;組件需要重新加載&#xff0c;即重新走一遍組件的生命周期常見解決方案&#xff1a; 使用v-if指令&#xff1a;v-if 可以實現 true (加載)和 false (卸載) async reloadComponent() {this.show false// 加上 nextTick this.$nextTick(function() {this.show…

《圖解HTTP》 學習日記

1.了解WEB以及網絡基礎 1.1使用HTTP協議訪問WEB web頁面顯示:根據web瀏覽器地址欄中輸入指定的URL,web瀏覽器從web服務端獲取文件資源(resource)等信息&#xff0c;從而顯示出web頁面 1.2網絡基礎TCP/IP 通常使用的網絡(包括 互聯網)是在tcp/ip協議族的基礎上運作的&#xf…

【Docker】docker compose 安裝 Redis Stack

注&#xff1a;整理不易&#xff0c;請不要吝嗇你的贊和收藏。 前文 Redis Stack 什么是&#xff1f; 簡單來說&#xff0c;Redis Stack 是增強版的 Redis &#xff0c;它在傳統的 Redis 數據庫基礎上增加了一些高級功能和模塊&#xff0c;以支持更多的使用場景和需求。Redis…

kubesphere前端源碼運行

一、下載源碼 源碼是react&#xff0c;下載地址是 GitHub - kubesphere/console at v3.3.2 然后直接用git下拉就可以了 下拉完成后差不多是這樣一個目錄結構&#xff0c;記得切分支到3.3.2 二、下載依賴 1、node & yurn 想要運行源碼首先需要node&#xff0c;使用剛才…

藍橋杯歷屆真題 #分布式隊列 (Java,C++)

文章目錄 題目解讀[藍橋杯 2024 省 Java B] 分布式隊列題目描述輸入格式輸出格式樣例 #1樣例輸入 #1樣例輸出 #1 提示 思路完整代碼 題目解讀 題目鏈接 [藍橋杯 2024 省 Java B] 分布式隊列 題目描述 小藍最近學習了一種神奇的隊列&#xff1a;分布式隊列。簡單來說&#x…

PySide6 Qt for Python Qt Quick參考網址

Qt QML BOOK&#xff1a; 《Qt for Python》 -Building an Application https://www.qt.io/product/qt6/qml-book/ch19-python-build-app#signals-and-slots Qt for Python&#xff1a;與C版本的差異即BUG處理&#xff08;常見的DLL文件確實的問題等&#xff09; Qt for Pyt…

如何選擇 Dockerfile 的放置方式

是否將 Dockerfile 放在項目根目錄下還是為每個應用服務單獨創建 Dockerfile&#xff0c;取決于項目架構和使用場景。以下是針對不同項目類型的最佳實踐和推薦方式&#xff1a; 一、單體應用項目 項目特點 項目是一個單體應用&#xff0c;只有一個運行環境&#xff0c;例如&a…

如何穩定使用 O1 / O1 Pro,讓“降智”現象不再困擾?

近期&#xff0c;不少朋友在使用 O1 或 O1 Pro 模型時&#xff0c;都會碰到“降智”或“忽高忽低”的智力波動&#xff0c;比如無法識圖、無法生成圖片、甚至回答準確度也不穩定。面對這些問題&#xff0c;你是不是也感到頭疼呢&#xff1f; 為了找到更可靠的解決辦法&#xf…

用戶界面的UML建模11

然而&#xff0c;在用戶界面方面&#xff0c;重要的是要了解《boundary》類是如何與這個異常分層結構進行關聯的。 《exception》類的對象可以作為《control》類的對象。因此&#xff0c;《exception》類能夠聚合《boundary》類。 參見圖12&#xff0c;《exception》Database…

稀疏編碼 (Sparse Coding) 算法詳解與PyTorch實現

稀疏編碼 (Sparse Coding) 算法詳解與PyTorch實現 目錄 稀疏編碼 (Sparse Coding) 算法詳解與PyTorch實現1. 稀疏編碼 (Sparse Coding) 算法概述1.1 稀疏表示1.2 稀疏編碼的優勢2. 稀疏編碼的核心技術2.1 稀疏編碼的目標2.2 稀疏編碼的優化2.3 基向量的學習3. PyTorch實現稀疏編…

記錄一次面試中被問到的問題 (HR面)

文章目錄 一、你對公司的了解多少二、為什么對這個崗位感興趣三、不能說的離職原因四、離職原因高情商回復五、你的核心優勢是什么六、你認為你比其他面試候選人的優勢是什么七、不要提及情感 一、你對公司的了解多少 準備要點&#xff1a; 在面試前&#xff0c;對公司進行充分…

uniapp獲取安卓與ios的唯一標識

uniapp獲取安卓與ios的唯一標識 代碼如下&#xff1a; const info uni.getSystemInfoSync(); const platform info.platform; // 手機安卓還是蘋果if (info.platform ios) {// iosconsole.log(iOS 設備唯一標識:, info.deviceId);} else{//安卓console.log(系統信息:, use…

前端 圖片上鼠標畫矩形框,標注文字,任意刪除

效果&#xff1a; 頁面描述&#xff1a; 對給定的幾張圖片&#xff0c;每張能用鼠標在圖上畫框&#xff0c;標注相關文字&#xff0c;框的顏色和文字內容能自定義改變&#xff0c;能刪除任意畫過的框。 實現思路&#xff1a; 1、對給定的這幾張圖片&#xff0c;用分頁器綁定…

前端學習匯總

一、打包工具 1.1、vite vite&#xff1a;vite -- 開發環境 熱更新_vite 熱更新-CSDN博客 1.2、webpack 常用loader&#xff1a;webpack基礎---常用loader_webpack 常用loader-CSDN博客 loader&#xff1a;webpack4和webpack5區別1---loader_webpack4與webpack5處理圖片的…

ARIMA模型 (AutoRegressive Integrated Moving Average) 算法詳解與PyTorch實現

ARIMA模型 (AutoRegressive Integrated Moving Average) 算法詳解與PyTorch實現 目錄 ARIMA模型 (AutoRegressive Integrated Moving Average) 算法詳解與PyTorch實現1. ARIMA模型概述1.1 時間序列預測1.2 ARIMA的優勢2. ARIMA的核心技術2.1 自回歸 (AR)2.2 差分 (I)2.3 移動平…

如果商品信息更新,爬蟲會失效嗎?

當商品信息更新時&#xff0c;爬蟲是否失效取決于更新的具體內容。以下是一些可能影響爬蟲的因素&#xff1a; 可能導致爬蟲失效的情況 HTML結構變化&#xff1a;如果 yiwugo 平臺更新了商品詳情頁面的 HTML 結構&#xff0c;比如改變了元素的標簽、類名或 ID&#xff0c;那么…