Skip to content

Commit

Permalink
image/color: tweak the formula for converting to gray.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
nigeltao committed Oct 20, 2016
1 parent a190f3c commit 584e3ea
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
23 changes: 20 additions & 3 deletions src/image/color/color.go
Expand Up @@ -246,16 +246,33 @@ 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 {
if _, ok := c.(Gray16); ok {
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)}
}

Expand Down
5 changes: 5 additions & 0 deletions src/image/color/ycbcr.go
Expand Up @@ -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
Expand All @@ -32,13 +34,16 @@ 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
} else {
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
Expand Down

0 comments on commit 584e3ea

Please sign in to comment.