Как извлечь массив двоичных значений из CGImage в Swift

Я использую функцию getPixelColor (распечатанную ниже) для извлечения всех данных пикселей из CGImage:

extension CGImage{
    func getPixelColor(pos: CGPoint) -> UIColor {

        let pixelData = self.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let pixelInfo: Int = ((Int(self.width) * Int(pos.y)) + Int(pos.x)) * 4

        let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }
}

Затем я усредняю ​​значения пикселей R, G, B для расчета интенсивности. Я помещаю каждое значение интенсивности в двумерный массив с размерами imageWidth x imageHeight. Затем эти значения интенсивности проверяются на соответствие определенному порогу и соответственно им присваивается либо ноль, либо единица.

extension UIColor {
    var coreImageColor: CIColor {
        return CIColor(color: self)
    }
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        let color = coreImageColor
        return (color.red, color.green, color.blue, color.alpha)
    }
}

var intensityArray: [[Float]] = Array(repeating: Array(repeating: 0, count: width), count: height)
//fill intensity array with a nested for loop

for index1 in 0...height-1{  //make sure height-1 is here always

    for index2 in 0...width-1{  //(width-1) has to goes here because the last place in the array is the size-1
        let cgPoint = CGPoint(x: CGFloat(index2), y: CGFloat(index1))  //get an "index out of range error at 250 times
        let color = cgImage?.getPixelColor(pos: cgPoint)
        let CIcolor = color?.coreImageColor
        let greencomponent = Float((color?.components.green)!)
        let redcomponent = Float((color?.components.red)!)
        let bluecomponent = Float((color?.components.blue)!)
        let alphacomponent = Float((color?.components.alpha)!)
        var intensity = (greencomponent+redcomponent+bluecomponent)/3
        if intensity > 0.9 {
            intensity = 1
        } else {
            intensity = 0
        }
        intensityArray[index1][index2] = intensity 
    }
}

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


person Amir Dailamy    schedule 17.01.2017    source источник


Ответы (1)


Посмотрел немного глубже, и мое предположение не было серьезной проблемой. Основная проблема заключалась во всех дополнительных объектах, которые вы создаете (здесь вы делаете слишком много работы). Вам не нужно создавать UIColor и CIColor для каждого пикселя только для того, чтобы извлечь обратно уже имеющуюся информацию.

Вот эскиз, слегка протестированный (может быть ошибка в расчетах, вам придется его протестировать)

var intensityArray: [[Double]] = Array(repeating: Array(repeating: 0, count: width), count: height)
//fill intensity array with a nested for loop

for index1 in 0...height-1{  //make sure height-1 is here always

    for index2 in 0...width-1{  //(width-1) has to goes here because the last place in the array is the size-1
        let colorIndex = (index1 * width + index2) * 4
        let red = Double(data[colorIndex]) / 255
        let green = Double(data[colorIndex + 1]) / 255
        let blue = Double(data[colorIndex + 2]) / 255

        var intensity = (red+green+blue)/3
        if intensity > 0.9 {
            intensity = 1
        } else {
            intensity = 0
        }
        intensityArray[index1][index2] = intensity
    }
}

Это все еще далеко от самого быстрого подхода, но это намного быстрее, чем вы делаете это. Если вам нужно, чтобы это было действительно быстро, вам следует взглянуть на CIImage и CIFilter, в которые встроены такие инструменты (хотя я не искал, как выполнить эту конкретную операцию; зависит от того, что вы планирую делать с интенсивами).


СТАРЫЙ ОТВЕТ; полезно, но может быть или не быть основной причиной этого.

Я собираюсь пойти на риск (слишком ленив, чтобы на самом деле бросить это в профилировщик) и предположить, что именно эта строка занимает почти все ваше время:

    intensityArray[index1][index2] = intensity 

Это не «двухмерный массив». В Свифте такого нет. Это массив массивов, а это разные вещи (каждая строка может быть разной длины). Скорее всего, это приведет к множеству операций копирования при записи, которые дублируют весь массив.

Я бы упростил его до [Float] и просто написал бы intensityArray[index1 + index2*width]. Это должно быть очень быстро (на самом деле, вы можете просто использовать .append вместо подписки). Когда вы закончите, вы можете упаковать его другим способом, если хотите.

Но урок здесь в том, что вы всегда должны начинать с профилирования с помощью инструментов. Не гадайте, в чем проблема (да, я догадывался, но я видел эту проблему много раз), тестируйте.

Кроме того, дважды проверьте, работает ли это значительно быстрее в режиме Release. Иногда такие вещи очень медленны в отладке, но хорошо, когда вы оптимизируете.

person Rob Napier    schedule 18.01.2017
comment
Как вы предлагаете объявить свой массив? Я не совсем понимаю, как: интенсивностьArray[index1 + index2*width] учитывает двумерность изображения. - person Amir Dailamy; 18.01.2017
comment
Создайте массив как размер высоташирина. Именно так двумерные массивы на самом деле реализованы в C (и большинстве языков). a[x][y] — это просто синтаксический сахар для a[x+widthy]. Настоящий двумерный массив (где все строки имеют одинаковую ширину) аналогичен одномерному массиву продукта. - person Rob Napier; 18.01.2017
comment
Как сейчас, позволяет ли colorIndex сканировать изображение слева направо, сверху вниз? Устранение лишних объектов значительно сократило время выполнения, спасибо. - person Amir Dailamy; 23.01.2017
comment
Да; эти данные расположены слева направо, сверху вниз. - person Rob Napier; 23.01.2017