为什么 CGImageGetBytesPerRow() 方法 return 在某些图像上是一个奇怪的值?

why the CGImageGetBytesPerRow() method return a weird value on some images?

我从

的一张更大的图片中得到了一张图片
let partialCGImage = CGImageCreateWithImageInRect(CGImage, frame)

但有时我会得到错误的 RGBA 值。例如,我计算了图像的平均红色值,但结果却像灰色图像。 所以我检查了如下信息。

image width: 64
image height: 64
image has 5120 bytes per row
image has 8 bits per component
image color space: <CGColorSpace 0x15d68fbd0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1)
image is mask: false
image bitmap info: CGBitmapInfo(rawValue: 8194)
image has 32 bits per pixel
image utt type: nil
image should interpolate: true
image  rendering intent: CGColorRenderingIntent
Bitamp Info: ------
Alpha info mask: Ture
Float components: False
Byte oder mask: Ture
Byte order default: False
Byte order 16 little: False
Byte order 32 little: Ture
Byte order 16 big: Ture
Byte order 32 big: False
Image Info ended---------------

然后我遇到了一个很奇怪的问题,为什么宽和高都是64px,图像每个分量8位(1字节)(每个像素4字节),但是每行字节是5120?
我注意到普通图像的位图信息完全不同,它没有任何字节顺序信息。 我用谷歌搜索了小端和大端之间的区别,但当它们一起出现时我感到困惑。 我真的需要帮助,因为我的项目已经因此延迟了 2 天。谢谢!

顺便说一下,我使用了以下代码来获取 RGBA 值。

let pixelData=CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage))
let data:UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

var rs: [[Int]] = []
var gs: [[Int]] = []
var bs: [[Int]] = []

let widthMax = imageWidth
let heightMax = imageHeight
for indexX in 0...widthMax {
        var tempR: [Int] = []
        var tempG: [Int] = []
        var tempB: [Int] = []
        for indexY in 0...heightMax {
            let offSet = 4 * (indexX * imageWidth + indexY)
            **let r = Int(data[pixelInfo + offSet])
            let g = Int(data[pixelInfo + 1 + offSet])
            let b = Int(data[pixelInfo + 2 + offSet])**
            tempR.append(r)
            tempG.append(g)
            tempB.append(b)
        }
        rs.append(tempR)
        gs.append(tempG)
        bs.append(tempB)
    }

如果我的代码有问题,请问我。谢谢你的帮助。

首先你还没有初始化 pixelInfo 变量。其次,您没有使用 A 值将所有内容向左移动 8 位。此外,我认为您不需要 pixelInfooffset,这两个变量是相同的,因此请将其中一个变量设置为等于您为 offset

编写的内容

除了Segmentation提出的关于pixelInfo的问题外,offSet的计算看起来很奇怪:

let offSet = 4 * (indexX * imageWidth + indexY)

xy 值是倒过来的。此外,您也不能假设每行的字节数始终等于像素宽度的 4 倍,因为某些图像格式每行填充字节数。不管怎样,理论上应该是:

let offSet = indexY * bytesPerRow + indexX * bytesPerPixel 

另请注意,除了 x/y 翻转问题之外,您不需要 0 ... widthMax0 ... heightMax(因为它们会 return widthMax + 1heightMax + 1 个数据点)。相反,您想使用 0 ..< widthMax0 ..< heightMax.

此外,如果您处理的是随机图像文件,这里还有其他更深层次的问题。例如,您不能对位图信息字段中捕获的 RGBA 与 ARGB 与 CMYK、大端与小端等做出假设。

与其编写可以处理像素缓冲区中所有这些变化的代码,Apple 建议采用替代方法来获取一些随机配置的图像并将其渲染为一些一致的上下文配置,然后您可以更轻松地导航缓冲区.参见 Technical Q&A #1509

每行字节数为 5120,因为您在较大的图像上使用了 CGImageCreateWithImageInRectFrom the CGImage reference manual:

The resulting image retains a reference to the original image, which means you may release the original image after calling this function.

新图像使用与旧(较大)图像相同的像素存储。这就是为什么新图像保留旧图像,以及它们每行字节数相同的原因。

至于为什么你没有得到你期望的红色值:Rob 的回答有一些有用的信息,但如果你想更深入地探索,请考虑你的位图信息是 8194 = 0x2002。

print(CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)

# Output:
8194

这些位决定了位图的字节顺序。但是这些名字并不是那么有用。让我们弄清楚这些位的字节顺序:

let context = CGBitmapContextCreate(nil, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)!
UIGraphicsPushContext(context)
let d: CGFloat = 255
UIColor(red: 1/d, green: 2/d, blue: 3/d, alpha: 1).setFill()
UIRectFill(.infinite)
UIGraphicsPopContext()
let data = UnsafePointer<UInt8>(CGBitmapContextGetData(context))
for i in 0 ..< 4 {
    print("\(i): \(data[i])")
}

# Output:
0: 3
1: 2
2: 1
3: 255

所以我们可以看到8194的位图信息意味着字节顺序是BGRA。您的代码假定它是 RGBA。