Версия Swift получает неправильные данные о пикселях, но версия Object-c получает данные о правильных пикселях из того же изображения, используя метод "CGBitmapContextCreate" - PullRequest
1 голос
/ 28 июня 2019

Я не могу поверить своим глазам, это в основном один и тот же код, просто преобразуйте код Object-c в быстрый код, но код Object-c всегда получает правильный ответ, но иногда быстрый код получает правильный ответ иногда ошибается.

Свифт-исполнение:

class ImageProcessor1 {
    class func processImage(image: UIImage) {
        guard let cgImage = image.cgImage else {
            return
        }
        let width = Int(image.size.width)
        let height = Int(image.size.height)
        let bytesPerRow = width * 4
        let imageData = UnsafeMutablePointer<UInt32>.allocate(capacity: width * height)
        let colorSpace = CGColorSpaceCreateDeviceRGB()

        let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue
        guard let imageContext = CGContext(data: imageData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
            return
        }
        imageContext.draw(cgImage, in: CGRect(origin: .zero, size: image.size))
        print("---------data from Swift version----------")
        for i in 0..<width * height {
            print(imageData[i])
        }
    }
}

Представление Objective-C:

- (UIImage *)processUsingPixels:(UIImage*)inputImage {

  // 1. Get the raw pixels of the image
  UInt32 * inputPixels;

  CGImageRef inputCGImage = [inputImage CGImage];
  NSUInteger inputWidth = CGImageGetWidth(inputCGImage);
  NSUInteger inputHeight = CGImageGetHeight(inputCGImage);

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

  NSUInteger bytesPerPixel = 4;
  NSUInteger bitsPerComponent = 8;

  NSUInteger inputBytesPerRow = bytesPerPixel * inputWidth;

  inputPixels = (UInt32 *)calloc(inputHeight * inputWidth, sizeof(UInt32));

  CGContextRef context = CGBitmapContextCreate(inputPixels, inputWidth, inputHeight,
                                               bitsPerComponent, inputBytesPerRow, colorSpace,
                                               kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

  CGContextDrawImage(context, CGRectMake(0, 0, inputWidth, inputHeight), inputCGImage);

    NSLog(@"---------data from Object-c version----------");
    UInt32 * currentPixel = inputPixels;
    for (NSUInteger j = 0; j < inputHeight; j++) {
        for (NSUInteger i = 0; i < inputWidth; i++) {
            UInt32 color = *currentPixel;
            NSLog(@"%u", color);
            currentPixel++;
        }
    }
  return inputImage;
}

Доступно на https://github.com/tuchangwei/Pixel

И если вы получите тот же ответ, пожалуйста, запустите его еще раз.

1 Ответ

1 голос
/ 28 июня 2019

И ваш код Objective-C и Swift имеют утечки . Также ваш код Swift не инициализирует выделенную память. Когда я инициализировал память, я не видел никаких отличий:

imageData.initialize(repeating: 0, count: width * height)

FWIW, в то время как allocate не инициализирует буфер памяти, calloc делает:

... Выделенная память заполнена байтами нулевого значения.

Но лично я бы посоветовал вам вообще отказаться от выделения памяти и передать nil для параметра data, а затем использовать bindMemory для доступа к этому буферу. Если вы сделаете это, как указано в , документация гласит:

Передайте NULL, если вы хотите, чтобы эта функция выделяла память для растрового изображения. Это освобождает вас от управления собственной памятью, что уменьшает проблемы с утечкой памяти.

Таким образом, возможно:

class func processImage(image: UIImage) {
    guard let cgImage = image.cgImage else {
        return
    }
    let width = cgImage.width
    let height = cgImage.height
    let bytesPerRow = width * 4

    let colorSpace = CGColorSpaceCreateDeviceRGB()

    let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue
    guard
        let imageContext = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
        let rawPointer = imageContext.data
    else {
        return
    }

    let pixelBuffer = rawPointer.bindMemory(to: UInt32.self, capacity: width * height)

    imageContext.draw(cgImage, in: CGRect(origin: .zero, size: CGSize(width: width, height: height)))
    print("---------data from Swift version----------")
    for i in 0..<width * height {
        print(pixelBuffer[i])
    }
}
...