]> Cypherpunks repositories - gostls13.git/commitdiff
image/color: tweak the formula for converting to gray.
authorNigel Tao <nigeltao@golang.org>
Thu, 20 Oct 2016 05:57:55 +0000 (16:57 +1100)
committerNigel Tao <nigeltao@golang.org>
Thu, 20 Oct 2016 22:27:28 +0000 (22:27 +0000)
This makes grayModel and gray16Model in color.go use the exact same
formula as RGBToYCbCr in ycbcr.go. They were the same formula in theory,
but in practice the color.go versions used a divide by 1000 and the
ycbcr.go versions used a (presumably faster) shift by 16.

This implies the nice property that converting an image.RGBA to an
image.YCbCr and then taking only the Y channel is equivalent to
converting an image.RGBA directly to an image.Gray.

The difference between the two formulae is non-zero, but small:
https://play.golang.org/p/qG7oe-eqHI

Updates #16251

Change-Id: I288ecb957fd6eceb9626410bd1a8084d2e4f8198
Reviewed-on: https://go-review.googlesource.com/31538
Reviewed-by: Rob Pike <r@golang.org>
src/image/color/color.go
src/image/color/ycbcr.go

index 104433974eab6a7f77f7da15089382507f556899..0832c597293751afe38b00f3b3811c60fdf31a2d 100644 (file)
@@ -246,8 +246,18 @@ func grayModel(c Color) Color {
                return c
        }
        r, g, b, _ := c.RGBA()
-       y := (299*r + 587*g + 114*b + 500) / 1000
-       return Gray{uint8(y >> 8)}
+
+       // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+       // as those given by the JFIF specification and used by func RGBToYCbCr in
+       // ycbcr.go.
+       //
+       // Note that 19595 + 38470 + 7471 equals 65536.
+       //
+       // The 24 is 16 + 8. The 16 is the same as used in RGBToYCbCr. The 8 is
+       // because the return value is 8 bit color, not 16 bit color.
+       y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
+
+       return Gray{uint8(y)}
 }
 
 func gray16Model(c Color) Color {
@@ -255,7 +265,14 @@ func gray16Model(c Color) Color {
                return c
        }
        r, g, b, _ := c.RGBA()
-       y := (299*r + 587*g + 114*b + 500) / 1000
+
+       // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+       // as those given by the JFIF specification and used by func RGBToYCbCr in
+       // ycbcr.go.
+       //
+       // Note that 19595 + 38470 + 7471 equals 65536.
+       y := (19595*r + 38470*g + 7471*b + 1<<15) >> 16
+
        return Gray16{uint16(y)}
 }
 
index 3df5d3675d6ef8231cdd2a30b18ea7848e0bd01e..c3658b0623d7b5684137a7df914c0197635926ba 100644 (file)
@@ -17,6 +17,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
        b1 := int32(b)
 
        // yy is in range [0,0xff].
+       //
+       // Note that 19595 + 38470 + 7471 equals 65536.
        yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
 
        // The bit twiddling below is equivalent to
@@ -32,6 +34,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
        // Note that the uint8 type conversion in the return
        // statement will convert ^int32(0) to 0xff.
        // The code below to compute cr uses a similar pattern.
+       //
+       // Note that -11056 - 21712 + 32768 equals 0.
        cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
        if uint32(cb)&0xff000000 == 0 {
                cb >>= 16
@@ -39,6 +43,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
                cb = ^(cb >> 31)
        }
 
+       // Note that 32768 - 27440 - 5328 equals 0.
        cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
        if uint32(cr)&0xff000000 == 0 {
                cr >>= 16