Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
danielchristianschroeter committed Dec 29, 2022
1 parent c216fc2 commit 59c2b65
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 1 deletion.
29 changes: 29 additions & 0 deletions .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: goreleaser

on:
pull_request:
push:
tags:
- '*'

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v3
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
watermark-to-image*
9 changes: 9 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
builds:
-
goos:
- darwin
- linux
- windows
goarch:
- amd64
- arm64
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# watermark-to-image
This application inserts a watermark in the lower right corner into the specified images folder.
This application inserts a watermark in the lower right corner into all images of an specified images folder.

# Examples
Check out examples/original_images and examples/watermarked_images to get an idea what the application is doing.

## Usage
1. Build or download prebuild executeable
2. Execute following command:
```
./watermark-to-image -sourceDirectory ./examples/original_images -targetDirectory ./examples/watermarked_images -watermarkImageFile ./examples/watermark.png
```

### Available command line parameter
```
./watermark-to-image --help
-sourceDirectory string
Source directory of original images
-targetDirectory string
Target directory for watermarked images
-watermarkImageFile string
Path and name of the watermark png image file
-watermarkIncreasement float
Scale factor of the watermark image file (default 100)
-watermarkMarginBottom int
Margin to the bottom edge for the watermark (default 20)
-watermarkMarginRight int
Margin to the right edge for the watermark (default 20)
-watermarkOpacity float
Opacity/Transparency of the watermark image file (default 0.5)
```

## Clone and build the project
```
$ git clone https://github.com/danielchristianschroeter/watermark-to-image
$ cd watermark-to-image
$ go build .
```
Binary file added examples/original_images/landscape.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/original_images/portrait.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/watermark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/watermarked_images/landscape.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/watermarked_images/portrait.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module watermark-to-image

go 1.19

require (
github.com/disintegration/imaging v1.6.2
github.com/gabriel-vasile/mimetype v1.4.1
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
golang.org/x/image v0.2.0
)

require golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
35 changes: 35 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q=
github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
212 changes: 212 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package main

import (
"flag"
"image"
"image/color"
"image/jpeg"
_ "image/png"
"log"
"os"
"path/filepath"
"strings"

"github.com/disintegration/imaging"
"github.com/gabriel-vasile/mimetype"
"github.com/rwcarlsen/goexif/exif"
"golang.org/x/image/draw"
)

// Used for build version information
var version = "development"

// Global variables used for command line flags
var (
sourceDirectory string
targetDirectory string
watermarkImageFile string
watermarkIncreasement float64
watermarkOpacity float64
watermarkMarginRight int
watermarkMarginBottom int
)

func init() {
// Initialize command line flags
flag.StringVar(&sourceDirectory, "sourceDirectory", "", "Source directory of original images")
flag.StringVar(&targetDirectory, "targetDirectory", "", "Target directory for watermarked images")
flag.StringVar(&watermarkImageFile, "watermarkImageFile", "", "Path and name of the watermark png image file")
flag.Float64Var(&watermarkIncreasement, "watermarkIncreasement", 100, "Scale factor of the watermark image file")
flag.Float64Var(&watermarkOpacity, "watermarkOpacity", 0.5, "Opacity/Transparency of the watermark image file")
flag.IntVar(&watermarkMarginRight, "watermarkMarginRight", 20, "Margin to the right edge for the watermark")
flag.IntVar(&watermarkMarginBottom, "watermarkMarginBottom", 20, "Margin to the bottom edge for the watermark")
}

func processImage(file *os.File, targetDirectory string, watermarkImageFile string, watermarkIncreasement float64, watermarkMarginRight int, watermarkMarginBottom int, watermarkOpacity float64) error {

// Load the watermark png image file using image.Decode
watermarkFile, err := os.Open(watermarkImageFile)
if err != nil {
log.Fatalf("Failed to os.Open of file %+v Error: %+v", watermarkImageFile, err)
}
defer watermarkFile.Close()
watermark, _, err := image.Decode(watermarkFile)
if err != nil {
log.Fatalf("Failed to image.Decode of file %+v Error: %+v", watermarkImageFile, err)
}

// Read EXIF-Metadata of image
exifData, err := exif.Decode(file)
if err != nil {
log.Fatalf("Failed to exif.Decode of file %+v Error: %+v", file.Name(), err)
}

// Read Orientation-Tag from EXIF-Metadata
orientation, err := exifData.Get(exif.Orientation)
if err != nil {
log.Fatalf("Failed to exifData.Get of file %+v Error: %+v", file.Name(), err)
}

// Convert Orientation-Tag to integer value
orientationInt, err := orientation.Int(0)
if err != nil {
log.Fatalf("Failed to convert orientation-tag of file %+v Error: %+v", file.Name(), err)
}

// Seek to the beginning of the file before calling image.Decode
_, err = file.Seek(0, 0)
if err != nil {
log.Fatalf("Failed to file.Seek of file %+v Error: %+v", file.Name(), err)
}

// Decode the image from the buffer
img, _, err := image.Decode(file)
if err != nil {
log.Fatalf("Failed to image.Decode of file %+v Error: %+v", file.Name(), err)
}
// Rotate and/or flip image based on Orientation-Tag from EXIF-Metadata
switch orientationInt {
case 1:
// no adjustment needed
case 2:
img = imaging.FlipH(img)
case 3:
img = imaging.Rotate(img, 180, color.Transparent)
case 4:
img = imaging.FlipH(imaging.Rotate(img, 180, color.Transparent))
case 5:
img = imaging.FlipV(imaging.Rotate(img, 90, color.Transparent))
case 6:
img = imaging.Rotate(img, 270, color.Transparent)
case 7:
img = imaging.FlipV(imaging.Rotate(img, 270, color.Transparent))
case 8:
img = imaging.Rotate(img, 90, color.Transparent)
}

// Set the the margins (pixels).
//marginSide := 15
//marginBottom := 15

// Get the width and the height of our image.
photoWidth := img.Bounds().Dx()
photoHeight := img.Bounds().Dy()

// Get the width and the height of our watermark.
watermarkWidth := watermark.Bounds().Dx()
watermarkHeight := watermark.Bounds().Dy()

// Increase the dimensions of the watermark by a given percentage.
//percentage := 250 // increase dimensions
resizedWidth := int(float64(watermarkWidth) * (float64(watermarkIncreasement) / 100.0))
resizedHeight := int(float64(watermarkHeight) * (float64(watermarkIncreasement) / 100.0))
resizedWatermark := image.NewRGBA(image.Rect(0, 0, resizedWidth, resizedHeight))
draw.NearestNeighbor.Scale(resizedWatermark, resizedWatermark.Bounds(), watermark, watermark.Bounds(), draw.Over, nil)

// Figure out the dstX value.
dstX := photoWidth - resizedWidth - watermarkMarginRight

// Figure out the dstY value.
dstY := photoHeight - resizedHeight - watermarkMarginBottom

// Overlay the watermark onto the photo image with a specified opacity/transparency
dst := imaging.Overlay(img, resizedWatermark, image.Pt(dstX, dstY), watermarkOpacity)

// Save the new image
outFile, err := os.Create(targetDirectory + filepath.Base(file.Name()))
if err != nil {
// handle error
log.Fatalf("Failed to os.Create of file %+v Error: %+v", targetDirectory+filepath.Base(file.Name()), err)
}
defer outFile.Close()
jpeg.Encode(outFile, dst, nil)

log.Println("Saved watermarked image to " + targetDirectory + filepath.Base(file.Name()))

return nil
}

func main() {
log.SetFlags(0)
flag.Usage = func() {
log.Println("watermark-to-image. Version: " + version)
flag.PrintDefaults()
}
flag.Parse()

if len(sourceDirectory) == 0 || len(targetDirectory) == 0 || len(watermarkImageFile) == 0 {
log.Fatal("Usage: -sourceDirectory <sourceDirectory> -targetDirectory <targetDirectory> -watermarkImageFile <watermarkImageFile>")
}

// Add slash to source directory, if not exists
if !strings.HasSuffix(sourceDirectory, "/") {
sourceDirectory += "/"
}

// Add slash to target directory, if not exists
if !strings.HasSuffix(targetDirectory, "/") {
targetDirectory += "/"
}

log.Println("Processing imges from directory " + sourceDirectory + "...")
// Open the source directory
dir, err := os.Open(sourceDirectory)
if err != nil {
log.Fatal(err)
}
defer dir.Close()

// Get a list of all the files in the source directory
files, err := dir.Readdir(-1)
if err != nil {
log.Fatal(err)
}

// Loop through the images in the source directory
for _, file := range files {
// Open the image
f, err := os.Open(sourceDirectory + file.Name())
if err != nil {
log.Printf("Failed to os.Open of file %+v Error: %v", sourceDirectory+file.Name(), err)
continue
}
defer f.Close()

// Skip image/heic
mimeType, err := mimetype.DetectFile(sourceDirectory + file.Name())
if err != nil {
log.Printf("Failed to mimetype.DetectFile of file %+v Error: %v", sourceDirectory+file.Name(), err)
}
switch mimeType.String() {
case "image/heic":
log.Printf("Skipping %v Reason: heic not supported", sourceDirectory+file.Name())
continue
}

// Process the image
if err := processImage(f, targetDirectory, watermarkImageFile, watermarkIncreasement, watermarkMarginRight, watermarkMarginBottom, watermarkOpacity); err != nil {
log.Printf("Failed to processImage of file %+v Error: %v", sourceDirectory+file.Name(), err)
continue
}
}
}

0 comments on commit 59c2b65

Please sign in to comment.