1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:04:40 -07:00

image/color: tweak the formula for converting to gray.

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>
This commit is contained in:
Nigel Tao 2016-10-20 16:57:55 +11:00
parent a190f3c8a3
commit 584e3ea258
2 changed files with 25 additions and 3 deletions

View File

@ -246,8 +246,18 @@ func grayModel(c Color) Color {
return c return c
} }
r, g, b, _ := c.RGBA() 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 { func gray16Model(c Color) Color {
@ -255,7 +265,14 @@ func gray16Model(c Color) Color {
return c return c
} }
r, g, b, _ := c.RGBA() 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)} return Gray16{uint16(y)}
} }

View File

@ -17,6 +17,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
b1 := int32(b) b1 := int32(b)
// yy is in range [0,0xff]. // yy is in range [0,0xff].
//
// Note that 19595 + 38470 + 7471 equals 65536.
yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16 yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
// The bit twiddling below is equivalent to // 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 // Note that the uint8 type conversion in the return
// statement will convert ^int32(0) to 0xff. // statement will convert ^int32(0) to 0xff.
// The code below to compute cr uses a similar pattern. // 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 cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
if uint32(cb)&0xff000000 == 0 { if uint32(cb)&0xff000000 == 0 {
cb >>= 16 cb >>= 16
@ -39,6 +43,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
cb = ^(cb >> 31) cb = ^(cb >> 31)
} }
// Note that 32768 - 27440 - 5328 equals 0.
cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15 cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
if uint32(cr)&0xff000000 == 0 { if uint32(cr)&0xff000000 == 0 {
cr >>= 16 cr >>= 16