// ycbcr returns the YCbCr values for the given colour, converting to them if // necessary. func ycbcr(colour color.Color) (y, cb, cr uint8) { switch spec := colour.(type) { case color.YCbCr: return spec.Y, spec.Cb, spec.Cr default: r, g, b, _ := colour.RGBA() return color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) } }
func image2YCbCrs(src image.Image) (yCbCrs []color.YCbCr) { srcBounds := src.Bounds() for i := 0; i < srcBounds.Max.Y; i++ { for j := 0; j < srcBounds.Max.X; j++ { r, g, b, _ := src.At(j, i).RGBA() y, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) yCbCrs = append(yCbCrs, color.YCbCr{y, cb, cr}) } } return yCbCrs }
func ImageTransformByProfile(src_image image.Image, src_prof, dst_prof *Profile) (image.Image, error) { var dst_image image.Image rect := src_image.Bounds() width := rect.Dx() height := rect.Dy() colorModel := src_image.ColorModel() // 今のところ RGBA, YCbCr のみ対応 if (colorModel != color.YCbCrModel) && (colorModel != color.RGBAModel) { return nil, fmt.Errorf("ImageTransformByProfile: Unsupported ColorModel(%d)", colorModel) } var src_rgba *image.RGBA var src_ycbcr *image.YCbCr if colorModel == color.YCbCrModel { // YCbCr の場合は RGB に変換する src_ycbcr = src_image.(*image.YCbCr) src_rgba = image.NewRGBA(rect) DrawYCbCr(src_rgba, rect, src_ycbcr, image.Pt(0, 0)) } else { src_rgba = src_image.(*image.RGBA) // type assertions } transform := CreateTransform(src_prof, DATA_RGBA_8, dst_prof, DATA_RGBA_8) defer transform.DeleteTransform() if transform == nil { return nil, fmt.Errorf("ImageTransformByProfile: CreateTransform Failedl(%d)", colorModel) } dst_rgba := image.NewRGBA(rect) src_pix := src_rgba.Pix dst_pix := dst_rgba.Pix len_pix := len(src_pix) transform.DoTransform(src_pix, dst_pix, len_pix) // YCbCr の場合は RGB から戻す if colorModel == color.YCbCrModel { dst_ycbcr := image.NewYCbCr(rect, src_ycbcr.SubsampleRatio) var x int var y int for y = 0; y < height; y++ { for x = 0; x < width; x++ { r, g, b, _ := dst_rgba.At(x, y).RGBA() yy, cb, cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) yi := dst_ycbcr.YOffset(x, y) ci := dst_ycbcr.COffset(x, y) dst_ycbcr.Y[yi] = yy dst_ycbcr.Cb[ci] = cb dst_ycbcr.Cr[ci] = cr } } dst_image = image.Image(dst_ycbcr) } else { dst_image = image.Image(dst_rgba) } return dst_image, nil }
// toYCbCr converts the 8x8 region of m whose top-left corner is p to its // YCbCr values. func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) { b := m.Bounds() xmax := b.Max.X - 1 ymax := b.Max.Y - 1 for j := 0; j < 8; j++ { for i := 0; i < 8; i++ { r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA() yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) yBlock[8*j+i] = int(yy) cbBlock[8*j+i] = int(cb) crBlock[8*j+i] = int(cr) } } }
func delta(c1, c2 color.NRGBA) float64 { if c1.A == 0 && c2.A == 0 { return 0.0 } y1, cb1, cr1 := color.RGBToYCbCr(c1.R, c1.G, c1.B) y2, cb2, cr2 := color.RGBToYCbCr(c2.R, c2.G, c2.B) dy := (int(y1) - int(y2)) * 3 dcb := int(cb1) - int(cb2) dcr := int(cr1) - int(cr2) da := int(c1.A) - int(c2.A) // if c2.A == 233 && c1.A == 255 { // fmt.Println(dy, dcb, dcr, da) // } // if c1.A == 255 && c2.A == 255 && c2.R > 0 { // fmt.Println(c1, c2, dy, dcb, dcr, da, float64(dy*dy+dcb*dcb+dcr*dcr+da*da)/32.0) // } return float64(dy*dy+dcb*dcb+dcr*dcr+da*da) / 32.0 }
func FromImage(src image.Image) *image.YCbCr { r := src.Bounds() dst := image.NewYCbCr(src.Bounds(), image.YCbCrSubsampleRatio444) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { c := src.At(x, y) r, g, b, _ := c.RGBA() yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) i := dst.YOffset(x, y) dst.Y[i] = yy dst.Cb[i] = cb dst.Cr[i] = cr } } return dst }
func nYCbCrAModel(c color.Color) color.Color { switch c := c.(type) { case Color: return c case color.YCbCr: return Color{c, 0xff} } r, g, b, a := c.RGBA() // Convert from alpha-premultiplied to non-alpha-premultiplied. if a != 0 { r = (r * 0xffff) / a g = (g * 0xffff) / a b = (b * 0xffff) / a } y, u, v := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) return Color{color.YCbCr{Y: y, Cb: u, Cr: v}, uint8(a >> 8)} }
// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images. func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) { b := m.Bounds() xmax := b.Max.X - 1 ymax := b.Max.Y - 1 for j := 0; j < 8; j++ { sj := p.Y + j if sj > ymax { sj = ymax } offset := (sj-b.Min.Y)*m.Stride - b.Min.X*4 for i := 0; i < 8; i++ { sx := p.X + i if sx > xmax { sx = xmax } pix := m.Pix[offset+sx*4:] yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2]) yBlock[8*j+i] = int(yy) cbBlock[8*j+i] = int(cb) crBlock[8*j+i] = int(cr) } } }
func fillTestImage(im image.Image) { b := im.Bounds() if !b.Eq(testIm.Bounds()) { panic("Requested target image dimensions not equal reference image.") } src := testIm if dst, ok := im.(*image.YCbCr); ok { b := testIm.Bounds() for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { r, g, b, _ := src.At(x, y).RGBA() yp, cb, cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) dst.Y[dst.YOffset(x, y)] = yp off := dst.COffset(x, y) dst.Cb[off] = cb dst.Cr[off] = cr } } return } draw.Draw(im.(draw.Image), b, testIm, b.Min, draw.Src) }
func colorCheck(c color.Color) bool { r, g, b, _ := c.RGBA() _, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) return (86 <= cb) && (cb <= 117) && (140 <= cr) && (cr <= 168) }
func NewImageMap(img image.Image) (*ImageMap, error) { var im *ImageMap switch m := img.(type) { case *image.YCbCr: im = &ImageMap{ Pix: m.Y, YStride: m.YStride, XStride: 1, Width: img.Bounds().Dx(), Height: img.Bounds().Dy(), Stddev: 1.0, } var verticalRes, horizontalRes int switch m.SubsampleRatio { case image.YCbCrSubsampleRatio420: verticalRes = 2 horizontalRes = 2 case image.YCbCrSubsampleRatio422: verticalRes = 1 horizontalRes = 2 case image.YCbCrSubsampleRatio440: verticalRes = 2 horizontalRes = 1 case image.YCbCrSubsampleRatio444: verticalRes = 1 horizontalRes = 1 default: return nil, errors.New("unsupported YCbCr subsample ratio") } im.setter = func(x, y int, c color.Color) { r, g, b, _ := c.RGBA() yc, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) m.Y[y*m.YStride+x] = yc off := y/verticalRes*m.CStride + x/horizontalRes m.Cb[off] = cb m.Cr[off] = cr } case *image.RGBA: im = &ImageMap{ Pix: m.Pix[1:], YStride: m.Stride, XStride: 4, Width: img.Bounds().Dx(), Height: img.Bounds().Dy(), Stddev: 1.0, setter: m.Set, } case *image.Gray: im = &ImageMap{ Pix: m.Pix, YStride: m.Stride, XStride: 1, Width: img.Bounds().Dx(), Height: img.Bounds().Dy(), Stddev: 1.0, setter: m.Set, } default: return nil, errors.New("Unsupported image format") } m := -1.0 s := 0.0 count := 0 for y := 0; y < im.Height; y++ { for x := 0; x < im.Width; x++ { count++ v := float64(im.Pix[y*im.YStride+x*im.XStride]) oldM := m if oldM == -1 { m = v s = 0 } else { m = oldM + ((v - oldM) / float64(count)) s += (v - oldM) * (v - m) } } } stddev := math.Sqrt(s / float64(count-1)) im.Stddev = stddev return im, nil }
func TestGetPixel(t *testing.T) { var pg *pixelGetter // RGBA, NRGBA, RGBA64, NRGBA64 palette := []color.Color{ color.NRGBA{0, 0, 0, 0}, color.NRGBA{255, 255, 255, 255}, color.NRGBA{50, 100, 150, 255}, color.NRGBA{150, 100, 50, 200}, } images1 := []draw.Image{ image.NewRGBA(image.Rect(-1, -2, 3, 4)), image.NewRGBA64(image.Rect(-1, -2, 3, 4)), image.NewNRGBA(image.Rect(-1, -2, 3, 4)), image.NewNRGBA64(image.Rect(-1, -2, 3, 4)), image.NewPaletted(image.Rect(-1, -2, 3, 4), palette), } colors1 := []struct { c color.NRGBA px pixel }{ {color.NRGBA{0, 0, 0, 0}, pixel{0, 0, 0, 0}}, {color.NRGBA{255, 255, 255, 255}, pixel{1, 1, 1, 1}}, {color.NRGBA{50, 100, 150, 255}, pixel{0.196, 0.392, 0.588, 1}}, {color.NRGBA{150, 100, 50, 200}, pixel{0.588, 0.392, 0.196, 0.784}}, } for _, img := range images1 { pg = newPixelGetter(img) for _, k := range colors1 { for _, x := range []int{-1, 0, 2} { for _, y := range []int{-2, 0, 3} { img.Set(x, y, k.c) px := pg.getPixel(x, y) if !comparePixels(k.px, px, 0.005) { t.Errorf("getPixel %T %v %dx%d %v %v", img, k.c, x, y, k.px, px) } } } } } // Uniform (Generic) for _, k := range colors1 { img := image.NewUniform(k.c) pg = newPixelGetter(img) for _, x := range []int{-1, 0, 2} { for _, y := range []int{-2, 0, 3} { px := pg.getPixel(x, y) if !comparePixels(k.px, px, 0.005) { t.Errorf("getPixel %T %v %dx%d %v %v", img, k.c, x, y, k.px, px) } } } } // YCbCr colors2 := []struct { c color.NRGBA px pixel }{ {color.NRGBA{0, 0, 0, 255}, pixel{0, 0, 0, 1}}, {color.NRGBA{255, 255, 255, 255}, pixel{1, 1, 1, 1}}, {color.NRGBA{50, 100, 150, 255}, pixel{0.196, 0.392, 0.588, 1}}, } for _, k := range colors2 { img := image.NewYCbCr(image.Rect(-1, -2, 3, 4), image.YCbCrSubsampleRatio444) pg = newPixelGetter(img) for _, x := range []int{-1, 0, 2} { for _, y := range []int{-2, 0, 3} { iy := img.YOffset(x, y) ic := img.COffset(x, y) img.Y[iy], img.Cb[ic], img.Cr[ic] = color.RGBToYCbCr(k.c.R, k.c.G, k.c.B) px := pg.getPixel(x, y) if !comparePixels(k.px, px, 0.005) { t.Errorf("getPixel %T %v %dx%d %v %v", img, k.c, x, y, k.px, px) } } } } // Gray, Gray16 images2 := []draw.Image{ image.NewGray(image.Rect(-1, -2, 3, 4)), image.NewGray16(image.Rect(-1, -2, 3, 4)), } colors3 := []struct { c color.NRGBA px pixel }{ {color.NRGBA{0, 0, 0, 0}, pixel{0, 0, 0, 1}}, {color.NRGBA{255, 255, 255, 255}, pixel{1, 1, 1, 1}}, {color.NRGBA{50, 100, 150, 255}, pixel{0.356, 0.356, 0.356, 1}}, {color.NRGBA{150, 100, 50, 200}, pixel{0.337, 0.337, 0.337, 1}}, } for _, img := range images2 { pg = newPixelGetter(img) for _, k := range colors3 { for _, x := range []int{-1, 0, 2} { for _, y := range []int{-2, 0, 3} { img.Set(x, y, k.c) px := pg.getPixel(x, y) if !comparePixels(k.px, px, 0.005) { t.Errorf("getPixel %T %v %dx%d %v %v", img, k.c, x, y, k.px, px) } } } } } }