Skip to content

Commit

Permalink
gocrop CLI and API
Browse files Browse the repository at this point in the history
  • Loading branch information
H3Cki committed Mar 22, 2023
1 parent d0b534e commit 9868d7d
Show file tree
Hide file tree
Showing 35 changed files with 1,431 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
*_crop*
!*.go

# Binaries for programs and plugins
*.exe
*.exe~
Expand Down
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "CLI",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd",
"console": "integratedTerminal",
"args": ["i", "--suffix", "_c" , "../testdata/rect.png"]
}
]
}
287 changes: 286 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,286 @@
# croppingo
# gocrop

gocrop provides a CLI and an API for cropping transparent images.

# Installation
In order to use the API:
```
go get github.com/H3Cki/gocrop
```

In order to install the CLI:
```
go install github.com/H3Cki/gocrop@latest
```

# How it works
All transparent pixels (or at least those with alpha value higher than the provided threshold) are discarded in all directions, left, top, right, bottom. If padding option was used the cropped image will be extended by the provided padding amount, equally in all directions. Examples (with hacky border to visualize the image size better, I suggest opening the image anyways):

Original image:

<kbd>
<img src="https://hecki.codes/gocrop/circle.png">
</kbd>

Cropped image:

<kbd>
<img src="https://hecki.codes/gocrop/circle_cropped.png">
</kbd>

Cropped and padded image:

<kbd>
<img src="https://hecki.codes/gocrop/circle_cropped_padded.png">
</kbd>

# CLI Examples

### 1. Crop specific images and output them with `_cropped` suffix:

Directory tree before:
```
├─ img1.png
├─ img2.png
├─ img3.png
```

Directory tree after:
```
├─ img1.png
├─ img1_cropped.png
├─ img2.png
├─ img2_cropped.png
├─ img3.png
```

```cli
gocrop image --suffix _cropped img1.png img2.png
```

### 2. Crop all images which file name matches a regex, in specific directories and all their subdirectories, output results into `images/cropped`:

Directory tree before:
```
dir1/
├─ img1.png
├─ img2.gif
dir2/
├─ img3.gif
```

Directory tree after:
```
dir1/
├─ img1.png
├─ img2.gif
dir2/
├─ img3.gif
images/
├─ cropped/
│ ├─ img2.gif
│ ├─ img3.gif
```

```cli
gocrop directory --out_dir images/cropped --regex ^.*gif.*$ --recursive dir1 dir2
```

# API Examples

### 1. Cropping single image

Directory tree before:
```
images/
├─ test.png
```

Directory tree after:
```
images/
├─ test.png // image is overwritten
```


```go
import (
"fmt"

"github.com/H3Cki/gocrop/gocrop"
)

func main() {
// Load image, doing it this way assures the image is croppable
// and it's format is supported
croppable, err := gocrop.Load("images/test.png")
if err != nil {
fmt.Println(err)
return
}

// Create cropper with no options (source image will be overwritten)
cropper, _ := gocrop.NewCropper()
if err != nil {
fmt.Println(err)
return
}

// Crop the image
cropped, ok := cropper.Crop(croppable)
// If ok is false we can skip saving the image because no changes were made
if !ok {
fmt.Println("cropping would make no difference to target image")
return
}

// Encode and save the image at "images/test.png"
err = cropper.Save(cropped)
if err != nil {
fmt.Println(err)
return
}
}
```


### 2. Cropping single image with 10-pixel padding and saving it in another directory

Directory tree before:
```
images/
├─ test.png
```

Directory tree after:
```
images/
├─ test.png
cropped/
├─ test.png
```


```go
package main

import (
"fmt"

"github.com/H3Cki/gocrop/gocrop"
)

func main() {
// Load image, doing it this way assures the image is croppable
// and it's format is supported
croppable, err := gocrop.Load("images/test.png")
if err != nil {
fmt.Println(err)
return
}

// Create a cropper that saves the images in "images/cropped" directory (will be created if doesn't exist) and
// applies a 10px padding to the cropped image.
cropper, err := gocrop.NewCropper(
gocrop.WithOutDir("images/cropped"),
gocrop.WithPadding(10),
)
if err != nil {
fmt.Println(err)
return
}

// This time we used CropAndSave method for convenience.
err = cropper.CropAndSave(croppable)
if err != nil {
fmt.Println(err)
return
}
}
```


### 3. Cropping all images in all subdirectories and saving them with _cropped suffix in their original directory

Directory tree before:
```
images/
├─ image1.png
├─ avatars/
│ ├─ avatar1.png
│ ├─ avatar2.png
│ ├─ icons/
│ │ ├─ icon.png
```

Directory tree after:
```
images/
├─ image1.png
├─ image1_cropped.png
├─ avatars/
│ ├─ avatar1.png
│ ├─ avatar1_cropped.png
│ ├─ avatar2.png
│ ├─ avatar2_cropped.png
│ ├─ icons/
│ │ ├─ icon.png
│ │ ├─ icon_cropped.png
```


```go
package main

import (
"fmt"

"github.com/H3Cki/gocrop/gocrop"
)

func main() {
// Create a finder with recursive option to traverse all subdirectories
finder, err := gocrop.NewFinder(gocrop.WithRecursive(true))
if err != nil {
fmt.Println(err)
return
}

directories := []string{"."}

// Find all images in supported formats and wrap them in Croppable
croppables, err := finder.Find(directories)
if err != nil {
fmt.Println(err)
return
}

// Create a cropper with OutSuffix option that will append "_cropped" to the end of cropped images when saving them
cropper, err := gocrop.NewCropper(gocrop.WithOutSuffix("_cropped"))
if err != nil {
fmt.Println(err)
return
}

// Iterate over found croppables
for _, croppable := range croppables {
// Load the image (loads from croppable.Path and decodes it using croppable.Decode)
if err := croppable.Load(); err != nil {
continue
}

// Crop and save it
if err := cropper.CropAndSave(croppable); err != nil {
fmt.Printf("error cropping %s: %s\n", croppable.Path, err.Error())
}
}
}
```
18 changes: 18 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module github.com/H3Cki/gocrop

go 1.20

require (
github.com/stretchr/testify v1.8.2
github.com/urfave/cli/v2 v2.25.0
golang.org/x/image v0.6.0
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
58 changes: 58 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8=
github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
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.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
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/sync v0.1.0/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/sys v0.5.0/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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 9868d7d

Please sign in to comment.