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

时间:2016-04-17 08:22:37

标签: ios swift core-graphics

我通过

获得了更大图像的图像
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---------------

然后我遇到了一个非常奇怪的问题,为什么宽度和高度都是64像素,图像每个组件有8位(1个字节)(每个像素4个字节),但每行的字节数是5120? /> 我注意到普通图像的位图信息非常不同,它没有任何字节顺序信息。 我用Google搜索了小端和大端之间的差异,但是当它们一起出现时我感到困惑。 我真的需要帮助,因为我的项目已经推迟了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)
    }

问我你的代码是否有问题。谢谢你的帮助。

3 个答案:

答案 0 :(得分:3)

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

  

生成的图像保留对原始图像的引用,这意味着您可以在调用此函数后释放原始图像。

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

至于为什么你没有得到你期望的红色值: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。

答案 1 :(得分:2)

首先,您尚未初始化<form name="customersForm" ng-submit="findCustomers(custName, custCity)"> <button type=submit **ng-disabled="validateInputs(custName,custCity)"** id="listButton" ng-click="findCustomers(custName,custCity)"><span>Find </span></button> 变量。其次,你没有做任何事情,pixelInfo值将所有8位向左移动。此外,我认为您不需要ApixelInfo,这两个变量是相同的,因此请保持其中一个等于您为offset所写的内容

答案 2 :(得分:2)

除了pixelInfo的问题,由分段提出,offSet的计算似乎很奇怪:

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

xy值是向后的。此外,您也不能假设每行的字节总是等于宽度的4倍(因为某些图像格式每行填充字节)。无论如何,它理论上应该是:

let offSet = indexY * bytesPerRow + indexX * bytesPerPixel 

另请注意,除x / y翻转问题外,您不希望0 ... widthMax0 ... heightMax(因为这些会返回widthMax + 1heightMax + 1个数据点)。相反,您要使用0 ..< widthMax0 ..< heightMax

此外,如果您正在处理随机图像文件,此处还有其他更深层次的问题。例如,您无法对位图信息字段中捕获的RGBA与ARGB与CMYK,大端与小端等进行假设。

Apple不是编写可以处理像素缓冲区中所有这些变化的代码,而是建议使用某些随机配置的图像并将其渲染为某些一致的上下文配置,然后您可以更轻松地导航缓冲区。请参阅Technical Q&A #1509