Created
September 5, 2025 23:27
-
-
Save EliCDavis/e799d4eea016ba010700460d2548fea1 to your computer and use it in GitHub Desktop.
Flip Cr Cb in image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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