Skip to content

Instantly share code, notes, and snippets.

@EliCDavis
Created September 5, 2025 23:27
Show Gist options
  • Save EliCDavis/e799d4eea016ba010700460d2548fea1 to your computer and use it in GitHub Desktop.
Save EliCDavis/e799d4eea016ba010700460d2548fea1 to your computer and use it in GitHub Desktop.
Flip Cr Cb in image
package main
import (
"image"
"image/color"
"image/jpeg"
"image/png"
"log"
"os"
"path/filepath"
)
type BT struct {
A float64
B float64
C float64
D float64
E float64
}
var (
BT601 = BT{
A: 0.299,
B: 0.587,
C: 0.114,
D: 1.772,
E: 1.402,
}
BT709 = BT{
A: 0.2126,
B: 0.7152,
C: 0.0722,
D: 1.8556,
E: 1.5748,
}
BT2020 = BT{
A: 0.2627,
B: 0.6780,
C: 0.0593,
D: 1.8814,
E: 1.4746,
}
)
func clamp(v float64) uint8 {
if v < 0 {
return 0
}
if v > 255 {
return 255
}
return uint8(v + 0.5)
}
func processImage(img image.Image, space BT) image.Image {
bounds := img.Bounds()
out := image.NewRGBA(bounds)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
rr, gg, bb, aa := img.At(x, y).RGBA()
// Normalize to 0-255
r := float64(rr >> 8)
g := float64(gg >> 8)
b := float64(bb >> 8)
Y, Cb, Cr := RgbToYCbCr(r, g, b, space)
Cr, Cb = Cb, Cr
r2, g2, b2 := YCbCrToRgb(Y, Cb, Cr, space)
out.Set(x, y, color.RGBA{
R: clamp(r2),
G: clamp(g2),
B: clamp(b2),
A: uint8(aa >> 8),
})
}
}
return out
}
func RgbToYCbCr(r, g, b float64, space BT) (y, cb, cr float64) {
y = space.A*r + space.B*g + space.C*b
cb = (b - y) / space.D
cr = (r - y) / space.E
return
}
func YCbCrToRgb(y, cb, cr float64, space BT) (r, g, b float64) {
r = y + space.E*cr
g = y - (space.A*space.E/space.B)*cr - (space.C*space.D/space.B)*cb
b = y + space.D*cb
return
}
func main() {
inputPath := "smiling friends.png"
outputPath := "smiling friends conv.png"
// open input file
inFile, err := os.Open(inputPath)
if err != nil {
log.Fatal(err)
}
defer inFile.Close()
// decode input
img, format, err := image.Decode(inFile)
if err != nil {
log.Fatal(err)
}
log.Printf("Loaded %s as %s", inputPath, format)
// process image
out := processImage(img, BT709)
// save output
outFile, err := os.Create(outputPath)
if err != nil {
log.Fatal(err)
}
defer outFile.Close()
switch ext := filepath.Ext(outputPath); ext {
case ".png":
err = png.Encode(outFile, out)
case ".jpg", ".jpeg":
err = jpeg.Encode(outFile, out, &jpeg.Options{Quality: 95})
default:
log.Fatalf("unsupported output format: %s", ext)
}
if err != nil {
log.Fatal(err)
}
log.Printf("Wrote %s", outputPath)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment