因此为问题提供了一个UIImage,然后使用ImageCrop类对其进行裁剪。然后,当我将裁剪后的图像读入ImagePixelHelper类时,如果裁剪后的图像具有与预期数量不同的字节数,它将停止正常工作。 例如,裁剪UIImage之后,我们通过执行以下操作来计算图像数据数组的预期长度:
然后使用CFDataGetBytePointer(image.cgImage!.dataProvider!.data),并检查该数组的长度,我们发现大小相差很大。这意味着我们不知道图像的格式,因此我们无法获得像素颜色,因为它的格式未知。
注意:
两个类均经过测试,可以独立运行,但一起工作时会中断
/// Helper class for cropping images
class ImageCrop
{
/// Function crops the image to the given rect and returns a new image
///
/// - Parameter rect: The rect to crop to
/// - Returns: A new image which is cropped to the rect passed in
static func crop(image:UIImage, rect:CGRect) -> UIImage
{
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y,
width: image.size.width, height: image.size.height)
context?.clip(to: CGRect(x: 0, y: 0,
width: rect.size.width, height: rect.size.height + 1))
image.draw(in: drawRect)
let subImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return subImage!
}
}
-
/// Helper class finds pixels values within the given image
class ImagePixelHelper
{
/// The image to find pixels in
private let image: UIImage
/// The image data
private var data:UnsafePointer<UInt8>?
/// The expected length of the image data
private var expectedLengthA:Int?
/// The expected rgb length of the image
private var expectedLengthRGB:Int?
/// The expected rgba length of the image
private var expectedLengthRGBA:Int?
/// The actual number of bytes in the image
private var numBytes: CFIndex?
private var pixelData: CFData?
/// Default constructor for the ImagePixelHelper
/// - Parameter image: The image to find pixels in
init(image: UIImage)
{
self.image = image
}
/// Function loads all the image data for quick access later
func loadImageData() throws
{
// Get the image as a CGImage
let cgImage : CGImage = image.cgImage!
pixelData = cgImage.dataProvider?.data
// Get the pointer to the start of the array
data = CFDataGetBytePtr(self.pixelData)!
// Calculate the expected lengths
expectedLengthA = Int(image.size.width * image.size.height)
expectedLengthRGB = 3 * expectedLengthA!
expectedLengthRGBA = 4 * expectedLengthA!
// Get the length of the data
numBytes = CFDataGetLength(pixelData)
}
/// Function sets all member vars to nil to help speed up GC
func unloadImageData()
{
data = nil
expectedLengthA = nil
expectedLengthRGB = nil
expectedLengthRGBA = nil
numBytes = nil
}
/// Function gets the pixel colour from the given image using the provided x y coordinates
/// - Parameter pixelX: The X Pixel coordinate
/// - Parameter pixelY: The Y Pixel coordinate
/// - Parameter bgr: Whether we should return BGR, by default this is true so must be set if you want RGB
func getPixelValueFromImage(pixelX: Int, pixelY:Int, bgr: Bool = true) -> UIColor
{
// If we have all the required member vars for this operation
if let data = self.data,
let expectedLengthA = self.expectedLengthA,
let expectedLengthRGB = self.expectedLengthRGB,
let expectedLengthRGBA = self.expectedLengthRGBA,
let numBytes = self.numBytes
{
// Get the index of the pixel we want
let index = Int(image.size.width) * pixelY + pixelX
// Check the number of bytes
switch numBytes
{
case expectedLengthA:
return UIColor(red: 0, green: 0, blue: 0, alpha: CGFloat(data[index])/255.0)
case expectedLengthRGB:
if bgr
{
return UIColor(red: CGFloat(data[3*index+2])/255.0, green: CGFloat(data[3*index+1])/255.0, blue: CGFloat(data[3*index])/255.0, alpha: 1.0)
}
else
{
return UIColor(red: CGFloat(data[3*index])/255.0, green: CGFloat(data[3*index+1])/255.0, blue: CGFloat(data[3*index+2])/255.0, alpha: 1.0)
}
case expectedLengthRGBA:
if bgr
{
return UIColor(red: CGFloat(data[4*index+2])/255.0, green: CGFloat(data[4*index+1])/255.0, blue: CGFloat(data[4*index])/255.0, alpha: CGFloat(data[4*index+3])/255.0)
}
else
{
return UIColor(red: CGFloat(data[4*index])/255.0, green: CGFloat(data[4*index+1])/255.0, blue: CGFloat(data[4*index+2])/255.0, alpha: CGFloat(data[4*index+3])/255.0)
}
default:
// unsupported format
return UIColor.clear
}
}
else
{
// Something didnt load properly or has been destroyed
return UIColor.clear
}
}
}
因此,鉴于上述两个类,这是一些导致问题的代码的示例
let image = UIImage(named: "SomeImage")!
let croppedImage = ImageCrop.crop(image: image, rect: CGRect(x: 0, y: 0, width: 100, height: 100))
let imagePixelHelper = ImagePixelHelper(image: croppedImage)
for x in 0 ... Int(croppedImage.size.width)
{
for y in 0 ... Int(croppedImage.size.height)
{
let colour = imagePixelHelper.getPixelValueFromImage(pixelX: x, pixelY: y)
print(colour)
}
}
这将仅打印UIColour.CLEAR,因为croppedImage是未知格式/大小比我们预期的大。然后,在调试器中查看imagePixelHelper变量,对于100 * 100的图像,数据数组的预期长度如下:
然后实际找到的数据数组长度为41600,图像数据的数组大于预期的长度,这就是问题所在。 在快速文档中进行了一些挖掘之后,我们发现我们可能使用了错误类型的Graphics上下文来裁剪图像,这表明我们改为使用UIGraphicsImageRenderer。但是我很难找到/创建一个可行的裁剪实现。
TLDR:简而言之,如果首先使用ImageCrop类裁剪图像,则由于图像数据数组的大小与预期的计算大小不匹配,它将破坏ImagePixelHelper。