gpt4 book ai didi

image - Go 中意外/不准确的图像颜色转换

转载 作者:IT王子 更新时间:2023-10-29 01:47:13 24 4
gpt4 key购买 nike

根据 Python 的 PIL 和 ImageMagick 的颜色值,Go 从 JPEG 的 YCbCr 到 RGBA 的转换算法似乎略有偏差,但我可能只是忽略了一些东西。

PIL 和 IM 的结果是一样的。对于 Go,我加载图像,转换为非 alpha 预乘模型,然后直接访问字段,而不是使用 RGBA getter(将 alpha 与颜色分量相乘)。不幸的是,许多单独的分量值是相等的,但大多数颜色至少有一个分量与 PIL/IM 结果中同一位置的分量相差 +-1。

任何人都可以对此提供一些智慧/解释吗?

使用 ImageMagick(“转换 image.jpg image.txt”;左右 RGB 匹配,此处仅供引用):

# ImageMagick pixel enumeration: 100,67,255,srgb
0,0: (190,200,210) #BEC8D2 srgb(190,200,210)
1,0: (193,203,213) #C1CBD5 srgb(193,203,213)
2,0: (195,205,215) #C3CDD7 srgb(195,205,215)
3,0: (195,205,215) #C3CDD7 srgb(195,205,215)
4,0: (194,204,214) #C2CCD6 srgb(194,204,214)
5,0: (195,205,215) #C3CDD7 srgb(195,205,215)
6,0: (198,208,218) #C6D0DA srgb(198,208,218)
7,0: (200,210,220) #C8D2DC srgb(200,210,220)
8,0: (202,210,221) #CAD2DD srgb(202,210,221)
9,0: (203,211,222) #CBD3DE srgb(203,211,222)
10,0: (205,213,224) #CDD5E0 srgb(205,213,224)
11,0: (208,217,226) #D0D9E2 srgb(208,217,226)
12,0: (211,218,226) #D3DAE2 srgb(211,218,226)
13,0: (213,220,228) #D5DCE4 srgb(213,220,228)
14,0: (216,223,229) #D8DFE5 srgb(216,223,229)
15,0: (217,224,230) #D9E0E6 srgb(217,224,230)
16,0: (220,225,231) #DCE1E7 srgb(220,225,231)
17,0: (221,226,232) #DDE2E8 srgb(221,226,232)
18,0: (223,228,234) #DFE4EA srgb(223,228,234)
19,0: (224,229,235) #E0E5EB srgb(224,229,235)

使用 PIL:

(代码)

import os

import PIL.Image as Image

def _main():
image_filepath = 'image.jpg'
output_filepath = image_filepath + '.python-dump'

im = Image.open(image_filepath)
width, height = im.size

data = im.getdata()

if os.path.exists(output_filepath):
os.remove(output_filepath)

with open(output_filepath, 'w') as f:
for y in range(height):
for x in range(width):
r, g, b = data[y * im.size[0] + x]

s = '({}, {}): [{} {} {}]\n'.format(y, x, r, g, b)
f.write(s)

if __name__ == '__main__':
_main()

(输出)

(0, 0): [190 200 210]
(0, 1): [193 203 213]
(0, 2): [195 205 215]
(0, 3): [195 205 215]
(0, 4): [194 204 214]
(0, 5): [195 205 215]
(0, 6): [198 208 218]
(0, 7): [200 210 220]
(0, 8): [202 210 221]
(0, 9): [203 211 222]
(0, 10): [205 213 224]
(0, 11): [208 217 226]
(0, 12): [211 218 226]
(0, 13): [213 220 228]
(0, 14): [216 223 229]
(0, 15): [217 224 230]
(0, 16): [220 225 231]
(0, 17): [221 226 232]
(0, 18): [223 228 234]
(0, 19): [224 229 235]

使用 Go:

(代码)

package main

import (
"os"
"fmt"
"image"
"image/color"
"reflect"

_ "image/jpeg"
)

func main() {
imageFilepath := "image.jpg"
outputFilepath := imageFilepath + ".go-dump"

f, err := os.Open(imageFilepath)
if err != nil {
panic(err)
}

defer f.Close()

image, _, err := image.Decode(f)
if err != nil {
panic(err)
}

r := image.Bounds()
width := r.Max.X
height := r.Max.Y

os.Remove(outputFilepath)

g, err := os.OpenFile(outputFilepath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
panic(err)
}

defer g.Close()

for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
p := image.At(x, y)
c := color.NRGBAModel.Convert(p).(color.NRGBA)

s := fmt.Sprintf("(%d, %d): [%d %d %d %d]\n", y, x, c.R, c.G, c.B, c.A)
g.Write([]byte(s))
}
}
}

(输出)

(0, 0): [190 200 211 255]
(0, 1): [193 203 214 255]
(0, 2): [195 205 216 255]
(0, 3): [195 205 216 255]
(0, 4): [194 204 215 255]
(0, 5): [195 205 216 255]
(0, 6): [198 208 219 255]
(0, 7): [200 210 221 255]
(0, 8): [202 210 222 255]
(0, 9): [203 211 223 255]
(0, 10): [205 213 225 255]
(0, 11): [208 217 226 255]
(0, 12): [212 218 226 255]
(0, 13): [214 220 228 255]
(0, 14): [217 224 229 255]
(0, 15): [218 225 230 255]
(0, 16): [220 225 231 255]
(0, 17): [221 226 232 255]
(0, 18): [223 228 234 255]
(0, 19): [224 229 235 255]

编辑:哦,伙计。

Go 代码似乎以完全不同的方式实现 YCbCr->RGB 转换。它不仅表明它进行了一些小的舍入(偏离 JFIF 规范)以便它可以实现更快的整数数学,而不是 float 学,PIL/Pillow(和 IM,暗示)使用查找表而不是实际的算术。这最终似乎暗示 Go 永远不会产生与其他实现相同的颜色值。 如果 Go 和其他语言之间的颜色值相同至关重要,您可能需要使用替代实现。

Go 实现:

( https://golang.org/src/image/color/ycbcr.go )

// YCbCrToRGB converts a Y'CbCr triple to an RGB triple.
func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
// The JFIF specification says:
// R = Y' + 1.40200*(Cr-128)
// G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128)
// B = Y' + 1.77200*(Cb-128)
// http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
//
// Those formulae use non-integer multiplication factors. When computing,
// integer math is generally faster than floating point math. We multiply
// all of those factors by 1<<16 and round to the nearest integer:
// 91881 = roundToNearestInteger(1.40200 * 65536).
// 22554 = roundToNearestInteger(0.34414 * 65536).
// 46802 = roundToNearestInteger(0.71414 * 65536).
// 116130 = roundToNearestInteger(1.77200 * 65536).
//
// Adding a rounding adjustment in the range [0, 1<<16-1] and then shifting
// right by 16 gives us an integer math version of the original formulae.
// R = (65536*Y' + 91881 *(Cr-128) + adjustment) >> 16
// G = (65536*Y' - 22554 *(Cb-128) - 46802*(Cr-128) + adjustment) >> 16
// B = (65536*Y' + 116130 *(Cb-128) + adjustment) >> 16
// A constant rounding adjustment of 1<<15, one half of 1<<16, would mean
// round-to-nearest when dividing by 65536 (shifting right by 16).
// Similarly, a constant rounding adjustment of 0 would mean round-down.
//
// Defining YY1 = 65536*Y' + adjustment simplifies the formulae and
// requires fewer CPU operations:
// R = (YY1 + 91881 *(Cr-128) ) >> 16
// G = (YY1 - 22554 *(Cb-128) - 46802*(Cr-128)) >> 16
// B = (YY1 + 116130 *(Cb-128) ) >> 16
//
// The inputs (y, cb, cr) are 8 bit color, ranging in [0x00, 0xff]. In this
// function, the output is also 8 bit color, but in the related YCbCr.RGBA
// method, below, the output is 16 bit color, ranging in [0x0000, 0xffff].
// Outputting 16 bit color simply requires changing the 16 to 8 in the "R =
// etc >> 16" equation, and likewise for G and B.
//
// As mentioned above, a constant rounding adjustment of 1<<15 is a natural
// choice, but there is an additional constraint: if c0 := YCbCr{Y: y, Cb:
// 0x80, Cr: 0x80} and c1 := Gray{Y: y} then c0.RGBA() should equal
// c1.RGBA(). Specifically, if y == 0 then "R = etc >> 8" should yield
// 0x0000 and if y == 0xff then "R = etc >> 8" should yield 0xffff. If we
// used a constant rounding adjustment of 1<<15, then it would yield 0x0080
// and 0xff80 respectively.
//
// Note that when cb == 0x80 and cr == 0x80 then the formulae collapse to:
// R = YY1 >> n
// G = YY1 >> n
// B = YY1 >> n
// where n is 16 for this function (8 bit color output) and 8 for the
// YCbCr.RGBA method (16 bit color output).
//
// The solution is to make the rounding adjustment non-constant, and equal
// to 257*Y', which ranges over [0, 1<<16-1] as Y' ranges over [0, 255].
// YY1 is then defined as:
// YY1 = 65536*Y' + 257*Y'
// or equivalently:
// YY1 = Y' * 0x10101
yy1 := int32(y) * 0x10101
cb1 := int32(cb) - 128
cr1 := int32(cr) - 128

// The bit twiddling below is equivalent to
//
// r := (yy1 + 91881*cr1) >> 16
// if r < 0 {
// r = 0
// } else if r > 0xff {
// r = ^int32(0)
// }
//
// but uses fewer branches and is faster.
// Note that the uint8 type conversion in the return
// statement will convert ^int32(0) to 0xff.
// The code below to compute g and b uses a similar pattern.
r := yy1 + 91881*cr1
if uint32(r)&0xff000000 == 0 {
r >>= 16
} else {
r = ^(r >> 31)
}

g := yy1 - 22554*cb1 - 46802*cr1
if uint32(g)&0xff000000 == 0 {
g >>= 16
} else {
g = ^(g >> 31)
}

b := yy1 + 116130*cb1
if uint32(b)&0xff000000 == 0 {
b >>= 16
} else {
b = ^(b >> 31)
}

return uint8(r), uint8(g), uint8(b)
}

PIL(实际上是 Pillow)实现(使用查找表):

( https://github.com/python-pillow/Pillow/blob/bb1b3a532ca3fef915f9cde17ba2227671ac691c/libImaging/ConvertYCbCr.c#L363 )

void
ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels)
{
int x;
UINT8 a;
int r, g, b;
int y, cr, cb;

for (x = 0; x < pixels; x++, in += 4, out += 4) {

y = in[0];
cb = in[1];
cr = in[2];
a = in[3];

r = y + (( R_Cr[cr]) >> SCALE);
g = y + ((G_Cb[cb] + G_Cr[cr]) >> SCALE);
b = y + ((B_Cb[cb] ) >> SCALE);

out[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r;
out[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g;
out[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b;
out[3] = a;
}
}

最佳答案

请参阅上面@JimB 的评论。显然,规范不包括这种特殊的转换。因此,实际上,一种实现方式与另一种实现方式可能不同。

关于image - Go 中意外/不准确的图像颜色转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47550838/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com