From 602c4b09936fe587d304b1c5cd8cd804a23caa79 Mon Sep 17 00:00:00 2001 From: nxshock Date: Fri, 23 Jun 2017 19:18:15 +0500 Subject: [PATCH] Upload code --- README.md | 18 +++++++++++- colorcrop.go | 67 +++++++++++++++++++++++++++++++++++++++++++++ comparators.go | 32 ++++++++++++++++++++++ comparators_test.go | 41 +++++++++++++++++++++++++++ example/main.go | 41 +++++++++++++++++++++++++++ example_test.go | 29 ++++++++++++++++++++ 6 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 colorcrop.go create mode 100644 comparators.go create mode 100644 comparators_test.go create mode 100644 example/main.go create mode 100644 example_test.go diff --git a/README.md b/README.md index 10cc73c..12d0384 100644 --- a/README.md +++ b/README.md @@ -1 +1,17 @@ -# colorcrop \ No newline at end of file +# 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". diff --git a/colorcrop.go b/colorcrop.go new file mode 100644 index 0000000..88595a1 --- /dev/null +++ b/colorcrop.go @@ -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 +} diff --git a/comparators.go b/comparators.go new file mode 100644 index 0000000..f052973 --- /dev/null +++ b/comparators.go @@ -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 +} diff --git a/comparators_test.go b/comparators_test.go new file mode 100644 index 0000000..36ed640 --- /dev/null +++ b/comparators_test.go @@ -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)) + } + } + } +} diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..f15e0d9 --- /dev/null +++ b/example/main.go @@ -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) + } +} diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..92ed267 --- /dev/null +++ b/example_test.go @@ -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) +}