|
@@ -36,11 +36,6 @@ func applyColorMapping(img image.Image, fn func(c color.NRGBA) color.NRGBA) *ima
|
|
|
return dst
|
|
|
}
|
|
|
|
|
|
-// clamp & round float64 to uint8 (0..255)
|
|
|
-func clamp(v float64) uint8 {
|
|
|
- return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
|
|
|
-}
|
|
|
-
|
|
|
// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
|
|
|
// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
|
|
|
// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
|
|
@@ -64,6 +59,56 @@ func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
|
|
|
return applyColorMapping(img, fn)
|
|
|
}
|
|
|
|
|
|
+func sigmoid(a, b, x float64) float64 {
|
|
|
+ return 1 / (1 + math.Exp(b*(a-x)))
|
|
|
+}
|
|
|
+
|
|
|
+// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
|
|
|
+// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
|
|
|
+// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
|
|
|
+// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
|
|
|
+// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
|
|
|
+//
|
|
|
+// Examples:
|
|
|
+//
|
|
|
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // increase the contrast
|
|
|
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // decrease the contrast
|
|
|
+//
|
|
|
+func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
|
|
|
+ if factor == 0 {
|
|
|
+ return Clone(img)
|
|
|
+ }
|
|
|
+
|
|
|
+ lut := make([]uint8, 256)
|
|
|
+ a := math.Min(math.Max(midpoint, 0.0), 1.0)
|
|
|
+ b := math.Abs(factor)
|
|
|
+ sig0 := sigmoid(a, b, 0)
|
|
|
+ sig1 := sigmoid(a, b, 1)
|
|
|
+ e := 1.0e-6
|
|
|
+
|
|
|
+ if factor > 0 {
|
|
|
+ for i := 0; i < 256; i++ {
|
|
|
+ x := float64(i) / 255.0
|
|
|
+ sigX := sigmoid(a, b, x)
|
|
|
+ f := (sigX - sig0) / (sig1 - sig0)
|
|
|
+ lut[i] = clamp(f * 255.0)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for i := 0; i < 256; i++ {
|
|
|
+ x := float64(i) / 255.0
|
|
|
+ arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
|
|
|
+ f := a - math.Log(1.0/arg-1.0)/b
|
|
|
+ lut[i] = clamp(f * 255.0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn := func(c color.NRGBA) color.NRGBA {
|
|
|
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
|
|
|
+ }
|
|
|
+
|
|
|
+ return applyColorMapping(img, fn)
|
|
|
+}
|
|
|
+
|
|
|
// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
|
|
|
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
|
|
|
// The percentage = -100 gives solid grey image.
|