Получение пикселей в точке занимает слишком много времени для изображений HEIC - PullRequest
0 голосов
/ 07 июня 2019

Метод ниже получает цвет пикселя в определенной точке на выбранной фотографии.Когда выбран JPG, процесс обработки изображения ниже.Когда выбрана фотография HEIC, процесс изображения ниже медленный.Может быть примерно в 40 раз медленнее (7 секунд против 5 минут).

Мне было интересно, почему это так и что я могу сделать, чтобы это исправить?Просто чтобы быть понятным приведенный ниже код работает, просто требуется много времени для обработки HEIC.

class func getPixelColorAtPoint(point:CGPoint, sourceView: UIView) -> UIColor{

        let pixel = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
        var color: UIColor? = nil

        if let context = context {
            context.translateBy(x: -point.x, y: -point.y)
            sourceView.layer.render(in: context)

            color = UIColor(red: CGFloat(pixel[0])/255.0,
                            green: CGFloat(pixel[1])/255.0,
                            blue: CGFloat(pixel[2])/255.0,
                            alpha: CGFloat(pixel[3])/255.0)

            pixel.deallocate(capacity: 4)
        }
        return color!
    }

1 Ответ

0 голосов
/ 08 июня 2019

Если это занимает 5 минут, я полагаю, что вы должны вызывать эту процедуру несколько раз, каждый раз выполняя это sourceView. Вы должны визуализировать весь вид в полный пиксельный буфер один раз и только один раз, и тогда вы сможете эффективно извлечь цвет нескольких пикселей из этого буфера.

Например:

var data: Data?
var width: Int!
var height: Int!

func pixelColor(at point: CGPoint, for sourceView: UIView) -> UIColor? {
    if data == nil { fillBuffer(for: sourceView) }

    return data?.withUnsafeBytes { rawBufferPointer in
        let pixels = rawBufferPointer.bindMemory(to: Pixel.self)
        let pixel = pixels[Int(point.y) * width + Int(point.x)]
        return pixel.color
    }
}

func fillBuffer(for sourceView: UIView) {
    // get image snapshot

    let bounds = sourceView.bounds
    let format = UIGraphicsImageRendererFormat()
    format.scale = 1
    let image = UIGraphicsImageRenderer(bounds: bounds, format: format).image { _ in
        sourceView.drawHierarchy(in: bounds, afterScreenUpdates: false)
    }
    guard let cgImage = image.cgImage else { return }

    // prepare to get pixel buffer

    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
    width = Int(bounds.width)
    height = Int(bounds.height)

    // populate and save pixel buffer

    if let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4 * width, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) {
        context.draw(cgImage, in: bounds)
        data = context.data.flatMap { Data(bytes: $0, count: 4 * width * height) }
    }
}

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

Кстати, вместо того, чтобы перемещаться по байтам RGBA по отдельности, я думаю, что полезно использовать struct, как показано ниже, для визуализации немного более интуитивного кода:

struct Pixel {
    let red: UInt8
    let green: UInt8
    let blue: UInt8
    let alpha: UInt8
}

extension Pixel {
    var color: UIColor {
        return UIColor(red: CGFloat(red) / 255,
                       green: CGFloat(green) / 255,
                       blue: CGFloat(blue) / 255,
                       alpha: CGFloat(alpha) / 255)
    }
}

Более интересный вопрос, ИМХО, почему вы восстанавливаете цвет нескольких пикселей. Вы действительно нуждаетесь в цвете для отдельных пикселей, или вы делаете какие-то расчеты на основе результатов (например, гистограммы, средний цвет и т. Д.)? То есть если вам действительно нужны цвета только попиксельно, это нормально, но если есть более широкая проблема, которую вы пытаетесь решить, мы могли бы показать вам более эффективные подходы, чем обработка попиксельно. Они варьируются от распараллеленных подпрограмм, высокопроизводительной обработки изображений vImage и т. Д. Но мы не можем сказать больше, не зная, что вы делаете с этими повторными вызовами для получения цветов пикселей.

...