mirror of
https://github.com/nxshock/colorcrop.git
synced 2025-07-02 00:23:44 +05:00
Comparator rework and fixes
This commit is contained in:
parent
ab1c13e694
commit
eff44ff1cc
5 changed files with 117 additions and 16 deletions
|
@ -14,12 +14,18 @@ Import package with
|
||||||
import "github.com/nxshock/colorcrop"
|
import "github.com/nxshock/colorcrop"
|
||||||
```
|
```
|
||||||
|
|
||||||
Crop white borders with 50% of thresold:
|
Crop **white** borders with **50%** of thresold:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
croppedImage := colorcrop.Crop(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5)
|
croppedImage := colorcrop.Crop(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You may use custom comparator of colors:
|
||||||
|
|
||||||
|
```go
|
||||||
|
croppedImage := colorcrop.CropWithComparator(sourceImage, color.RGBA{255, 255, 255, 255}, 0.5, colorcrop.CmpCIE76)
|
||||||
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
See in "examples".
|
See in "examples".
|
||||||
|
|
74
colorconversion.go
Normal file
74
colorconversion.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package colorcrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rgbtoXYZ(r, g, b uint32) (x, y, z float64) {
|
||||||
|
varR := float64(r) / 255
|
||||||
|
varG := float64(g) / 255
|
||||||
|
varB := float64(b) / 255
|
||||||
|
|
||||||
|
if varR > 0.04045 {
|
||||||
|
varR = math.Pow((varR+0.055)/1.055, 2.4)
|
||||||
|
} else {
|
||||||
|
varR = varR / 12.92
|
||||||
|
}
|
||||||
|
|
||||||
|
if varG > 0.04045 {
|
||||||
|
varG = math.Pow((varG+0.055)/1.055, 2.4)
|
||||||
|
} else {
|
||||||
|
varG = varG / 12.92
|
||||||
|
}
|
||||||
|
|
||||||
|
if varB > 0.04045 {
|
||||||
|
varB = math.Pow((varB+0.055)/1.055, 2.4)
|
||||||
|
} else {
|
||||||
|
varB = varB / 12.92
|
||||||
|
}
|
||||||
|
|
||||||
|
varR = varR * 100
|
||||||
|
varG = varG * 100
|
||||||
|
varB = varB * 100
|
||||||
|
|
||||||
|
x = varR*0.4124 + varG*0.3576 + varB*0.1805
|
||||||
|
y = varR*0.2126 + varG*0.7152 + varB*0.0722
|
||||||
|
z = varR*0.0193 + varG*0.1192 + varB*0.9505
|
||||||
|
return x, y, z
|
||||||
|
}
|
||||||
|
|
||||||
|
func xyztoLAB(x, y, z float64) (l, a, b float64) {
|
||||||
|
refX, refY, refZ := 95.047, 100.000, 108.883 // Daylight, sRGB, Adobe-RGB, Observer D65, 2°
|
||||||
|
|
||||||
|
varX := x / refX
|
||||||
|
varY := y / refY
|
||||||
|
varZ := z / refZ
|
||||||
|
|
||||||
|
if varX > 0.008856 {
|
||||||
|
varX = math.Pow(varX, (1.0 / 3.0))
|
||||||
|
} else {
|
||||||
|
varX = (7.787 * varX) + (16.0 / 116.0)
|
||||||
|
}
|
||||||
|
if varY > 0.008856 {
|
||||||
|
varY = math.Pow(varY, (1.0 / 3.0))
|
||||||
|
} else {
|
||||||
|
varY = (7.787 * varY) + (16.0 / 116.0)
|
||||||
|
}
|
||||||
|
if varZ > 0.008856 {
|
||||||
|
varZ = math.Pow(varZ, (1.0 / 3.0))
|
||||||
|
} else {
|
||||||
|
varZ = (7.787 * varZ) + (16.0 / 116.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
l = (116 * varY) - 16
|
||||||
|
a = 500 * (varX - varY)
|
||||||
|
b = 200 * (varY - varZ)
|
||||||
|
|
||||||
|
return l, a, b
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorToLAB(color color.Color) (l, a, b float64) {
|
||||||
|
cr, cg, cb, _ := color.RGBA()
|
||||||
|
return xyztoLAB(rgbtoXYZ(cr, cg, cb))
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
// Crop returns cropped image with default comparator.
|
// Crop returns cropped image with default comparator.
|
||||||
func Crop(img image.Image, color color.Color, thresold float64) image.Image {
|
func Crop(img image.Image, color color.Color, thresold float64) image.Image {
|
||||||
return CropWithComparator(img, color, thresold, CmpRGBComponentsDiff)
|
return CropWithComparator(img, color, thresold, CmpRGBComponents)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CropWithComparator returns cropped image with specified comparator.
|
// CropWithComparator returns cropped image with specified comparator.
|
||||||
|
|
|
@ -6,27 +6,48 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// comparator is a function that returns a difference between two colors in
|
// comparator is a function that returns a difference between two colors in
|
||||||
// range 0.0..1.0.
|
// range 0.0..1.0 (0.0 - same colors, 1.0 - totally different colors).
|
||||||
type comparator func(color.Color, color.Color) float64
|
type comparator func(color.Color, color.Color) float64
|
||||||
|
|
||||||
// CmpSquareRGBComponentsDiff returns difference of two colors
|
// CmpEuclidean returns Euclidean difference of two colors.
|
||||||
func CmpSquareRGBComponentsDiff(color1 color.Color, color2 color.Color) float64 {
|
// https://en.wikipedia.org/wiki/Color_difference#Euclidean
|
||||||
|
func CmpEuclidean(color1 color.Color, color2 color.Color) float64 {
|
||||||
const maxDiff = 113509.94967402637 // Difference between black and white colors
|
const maxDiff = 113509.94967402637 // Difference between black and white colors
|
||||||
|
|
||||||
r1, g1, b1, _ := color1.RGBA()
|
r1, g1, b1, _ := color1.RGBA()
|
||||||
r2, g2, b2, _ := color2.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)+
|
return math.Sqrt(distance(float64(r2), float64(r1))+
|
||||||
math.Pow(float64(b2)-float64(b1), 2.0)) / maxDiff
|
distance(float64(g2), float64(g1))+
|
||||||
|
distance(float64(b2), float64(b1))) / maxDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmpRGBComponentsDiff returns difference of two colors.
|
// CmpRGBComponents returns RGB components difference of two colors.
|
||||||
func CmpRGBComponentsDiff(color1 color.Color, color2 color.Color) float64 {
|
func CmpRGBComponents(color1 color.Color, color2 color.Color) float64 {
|
||||||
const maxDiff = 765 // Difference between black and white colors
|
const maxDiff = 195075.0 // Difference between black and white colors
|
||||||
|
|
||||||
r1, g1, b1, _ := color1.RGBA()
|
r1, g1, b1, _ := color1.RGBA()
|
||||||
r2, g2, b2, _ := color2.RGBA()
|
r2, g2, b2, _ := color2.RGBA()
|
||||||
return math.Sqrt(math.Abs(float64(r2)-float64(r1))+
|
|
||||||
|
return (math.Abs(float64(r2)-float64(r1)) +
|
||||||
math.Abs(float64(g2)-float64(g1)) +
|
math.Abs(float64(g2)-float64(g1)) +
|
||||||
math.Abs(float64(b2)-float64(b1))) / maxDiff
|
math.Abs(float64(b2)-float64(b1))) / maxDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmpCIE76 returns difference of two colors defined in CIE76 standart.
|
||||||
|
// https://en.wikipedia.org/wiki/Color_difference#CIE76
|
||||||
|
func CmpCIE76(color1 color.Color, color2 color.Color) float64 {
|
||||||
|
const maxDiff = 150.8463301377893 // Difference between blue and white colors
|
||||||
|
|
||||||
|
r1, g1, b1, _ := color1.RGBA()
|
||||||
|
r2, g2, b2, _ := color2.RGBA()
|
||||||
|
|
||||||
|
cl1, ca1, cb1 := xyztoLAB(rgbtoXYZ(r1/255, g1/255, b1/255))
|
||||||
|
cl2, ca2, cb2 := xyztoLAB(rgbtoXYZ(r2/255, g2/255, b2/255))
|
||||||
|
|
||||||
|
return math.Sqrt(distance(cl2, cl1) + distance(ca2, ca1) + distance(cb2, cb1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func distance(x, y float64) float64 {
|
||||||
|
return (x - y) * (x - y)
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ func TestColorComparators(t *testing.T) {
|
||||||
color2 color.Color
|
color2 color.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
comparators := []comparator{CmpSquareRGBComponentsDiff, CmpRGBComponentsDiff}
|
comparators := []comparator{CmpEuclidean, CmpRGBComponents}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in In
|
in In
|
||||||
|
@ -33,8 +33,8 @@ func TestColorComparators(t *testing.T) {
|
||||||
|
|
||||||
for _, comparator := range comparators {
|
for _, comparator := range comparators {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if CmpSquareRGBComponentsDiff(test.in.color2, test.in.color1) != test.out {
|
if comparator(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))
|
t.Errorf("%s: %s: expected %.2f, got %.2f", runtime.FuncForPC(reflect.ValueOf(comparator).Pointer()).Name(), test.commentary, test.out, comparator(test.in.color2, test.in.color1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue