mirror of
https://github.com/nxshock/colorcrop.git
synced 2025-07-02 00:23:44 +05:00
Upload code
This commit is contained in:
parent
b055be1d8e
commit
602c4b0993
6 changed files with 227 additions and 1 deletions
18
README.md
18
README.md
|
@ -1 +1,17 @@
|
||||||
# colorcrop
|
# colorcrop
|
||||||
|
|
||||||
|
Go library for cropping images by removing borders with specified color.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`go get -u github.com/nxshock/colorcrop`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Crop white borders with 50% of thresold:
|
||||||
|
|
||||||
|
`croppedImage := colorcrop.Crop(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5)`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See in "examples".
|
||||||
|
|
67
colorcrop.go
Normal file
67
colorcrop.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// Go library for cropping images by removing borders with specified color.
|
||||||
|
package colorcrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Crop returns cropped image with default comparator.
|
||||||
|
func Crop(img image.Image, color color.Color, thresold float64) image.Image {
|
||||||
|
return CropWithComparator(img, color, thresold, CmpRGBComponentsDiff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crop returns cropped image with specified comparator.
|
||||||
|
func CropWithComparator(img image.Image, color color.Color, thresold float64, comparator comparator) image.Image {
|
||||||
|
return img.(interface {
|
||||||
|
SubImage(r image.Rectangle) image.Image
|
||||||
|
}).SubImage(cropRectanle(img, color, thresold, comparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cropRectanle returns rectangle of image without borders.
|
||||||
|
func cropRectanle(img image.Image, color color.Color, thresold float64, comparator comparator) image.Rectangle {
|
||||||
|
rectangle := img.Bounds()
|
||||||
|
|
||||||
|
TopLoop:
|
||||||
|
for y := rectangle.Min.Y; y < rectangle.Max.Y; y++ {
|
||||||
|
rectangle.Min.Y = y
|
||||||
|
for x := rectangle.Min.X; x < rectangle.Max.X; x++ {
|
||||||
|
if comparator(img.At(x, y), color) > thresold {
|
||||||
|
break TopLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BottomLoop:
|
||||||
|
for y := rectangle.Max.Y - 1; y >= rectangle.Min.Y; y-- {
|
||||||
|
rectangle.Max.Y = y + 1
|
||||||
|
for x := rectangle.Min.X; x < rectangle.Max.X; x++ {
|
||||||
|
if comparator(img.At(x, y), color) > thresold {
|
||||||
|
break BottomLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeftLoop:
|
||||||
|
for x := rectangle.Min.X; x < rectangle.Max.X; x++ {
|
||||||
|
rectangle.Min.X = x
|
||||||
|
for y := rectangle.Min.Y; y < rectangle.Max.Y; y++ {
|
||||||
|
if comparator(img.At(x, y), color) > thresold {
|
||||||
|
break LeftLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RightLoop:
|
||||||
|
for x := rectangle.Max.X - 1; x >= rectangle.Min.X; x-- {
|
||||||
|
rectangle.Max.X = x + 1
|
||||||
|
for y := rectangle.Min.Y; y < rectangle.Max.Y; y++ {
|
||||||
|
if comparator(img.At(x, y), color) > thresold {
|
||||||
|
break RightLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rectangle
|
||||||
|
}
|
32
comparators.go
Normal file
32
comparators.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package colorcrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// comparator is a function that returns a difference between two colors in
|
||||||
|
// range 0.0..1.0.
|
||||||
|
type comparator func(color.Color, color.Color) float64
|
||||||
|
|
||||||
|
// CmpColorDifference returns difference of two colors
|
||||||
|
func CmpSquareRGBComponentsDiff(color1 color.Color, color2 color.Color) float64 {
|
||||||
|
const maxDiff = 113509.94967402637 // Difference between black and white colors
|
||||||
|
|
||||||
|
r1, g1, b1, _ := color1.RGBA()
|
||||||
|
r2, g2, b2, _ := color2.RGBA()
|
||||||
|
return math.Sqrt(math.Pow(float64(r2)-float64(r1), 2.0)+
|
||||||
|
math.Pow(float64(g2)-float64(g1), 2.0)+
|
||||||
|
math.Pow(float64(b2)-float64(b1), 2.0)) / maxDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpColorDifference returns difference of two colors.
|
||||||
|
func CmpRGBComponentsDiff(color1 color.Color, color2 color.Color) float64 {
|
||||||
|
const maxDiff = 765 // Difference between black and white colors
|
||||||
|
|
||||||
|
r1, g1, b1, _ := color1.RGBA()
|
||||||
|
r2, g2, b2, _ := color2.RGBA()
|
||||||
|
return math.Sqrt(math.Abs(float64(r2)-float64(r1))+
|
||||||
|
math.Abs(float64(g2)-float64(g1))+
|
||||||
|
math.Abs(float64(b2)-float64(b1))) / maxDiff
|
||||||
|
}
|
41
comparators_test.go
Normal file
41
comparators_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package colorcrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestColorComparators(t *testing.T) {
|
||||||
|
type In struct {
|
||||||
|
color1 color.Color
|
||||||
|
color2 color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
comparators := []comparator{CmpSquareRGBComponentsDiff, CmpRGBComponentsDiff}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in In
|
||||||
|
out float64
|
||||||
|
commentary string
|
||||||
|
}{
|
||||||
|
{in: In{color.RGBA{0, 0, 0, 255}, color.RGBA{255, 255, 255, 255}},
|
||||||
|
out: 1.00,
|
||||||
|
commentary: "Difference between black and white colors"},
|
||||||
|
{in: In{color.RGBA{255, 255, 255, 255}, color.RGBA{255, 255, 255, 255}},
|
||||||
|
out: 0.00,
|
||||||
|
commentary: "Difference between same colors"},
|
||||||
|
{in: In{color.RGBA{255, 255, 255, 0}, color.RGBA{255, 255, 255, 255}},
|
||||||
|
out: 0.00,
|
||||||
|
commentary: "Difference between same colors with different transparency"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, comparator := range comparators {
|
||||||
|
for _, test := range tests {
|
||||||
|
if CmpSquareRGBComponentsDiff(test.in.color2, test.in.color1) != test.out {
|
||||||
|
t.Errorf("%s: %s: expected %.2f, got %.2f", runtime.FuncForPC(reflect.ValueOf(comparator).Pointer()).Name(), test.commentary, test.out, CmpSquareRGBComponentsDiff(test.in.color2, test.in.color1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
example/main.go
Normal file
41
example/main.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/nxshock/colorcrop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
// Read source image
|
||||||
|
sourceFile, err := os.Open("img.png")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
sourceImage, err := png.Decode(sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crop image white border with 50% thresold
|
||||||
|
croppedImage := colorcrop.Crop(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5)
|
||||||
|
|
||||||
|
// Save cropped image
|
||||||
|
croppedFile, err := os.Create("cropped.png")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
defer croppedFile.Close()
|
||||||
|
|
||||||
|
err = png.Encode(croppedFile, croppedImage)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
29
example_test.go
Normal file
29
example_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package colorcrop_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/nxshock/colorcrop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
// Read source image
|
||||||
|
sourceFile, _ := os.Open("img.png")
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
sourceImage, _ := png.Decode(sourceFile)
|
||||||
|
|
||||||
|
// Crop image white border with 50% thresold
|
||||||
|
croppedImage := colorcrop.Crop(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5)
|
||||||
|
|
||||||
|
// Save cropped image
|
||||||
|
croppedFile, _ := os.Create("cropped.png")
|
||||||
|
defer croppedFile.Close()
|
||||||
|
|
||||||
|
png.Encode(croppedFile, croppedImage)
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue