2014-12-12 3 views
2

Я пытаюсь реализовать вычисление гистограммы RGB для изображений в Swift (я новичок в iOS). Однако время вычисления для изображения 1500x1000 составляет около 66 секунд, что я считаю слишком медленным. Есть ли способы ускорить обход изображения?iOS slow image pixel iterating

P.S. текущий код заключается в следующем:

func calcHistogram(image: UIImage) { 
    let bins: Int = 20; 
    let width = Int(image.size.width); 
    let height = Int(image.size.height); 
    let binStep: Double = Double(bins-1)/255.0 
    var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int()))) 

    for i in 0..<bins { 
     for j in 0..<bins { 
      for k in 0..<bins { 
       hist[i][j][k] = 0; 
      } 
     } 
    } 

    var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage)) 
    var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) 
    for x in 0..<width { 
     for y in 0..<height { 
      var pixelInfo: Int = ((width * y) + x) * 4 
      var r = Double(data[pixelInfo]) 
      var g = Double(data[pixelInfo+1]) 
      var b = Double(data[pixelInfo+2]) 

      let r_bin: Int = Int(floor(r*binStep)); 
      let g_bin: Int = Int(floor(g*binStep)); 
      let b_bin: Int = Int(floor(b*binStep)); 
      hist[r_bin][g_bin][b_bin] += 1; 
     } 
    } 

} 
+2

Не ответ, но имея приложение iPhone рассмотреть все 1500 х 1000 пикселей не могут быть необходимыми. Вы можете нарисовать 10 000 случайных позиций из 'data' и создать из них достаточно хорошую гистограмму. –

+1

Или сделайте свою гистограмму с уменьшенным уменьшенным изображением изображения. Или сделайте свою гистограмму на графическом процессоре, который построен для такого рода параллельных вычислений данных. Или используйте библиотеку - например, CoreImage может генерировать гистограммы для вас. – rickster

+1

Используйте существующую реализацию. Я считаю, что библиотека CoreImage имеет функцию гистограммы. Или, если вам нужно, чтобы он был в режиме реального времени, проверьте пакет GPUImage. (Погугли это) –

ответ

4

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

Но даже если вы переходите к лучшему общему решению, например, к гистограмме на основе графического процессора, библиотеке или обоим ... Есть некоторые подводные ловушки Swift, в которые вы попадаете сюда, о которых хорошо говорить, t бежать в них в другом месте.

Во-первых, этот код:

var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int()))) 

for i in 0..<bins { 
    for j in 0..<bins { 
     for k in 0..<bins { 
      hist[i][j][k] = 0; 
     } 
    } 
} 

... инициализирует каждый член вашей 3D массива в два раза, с тем же результатом. Int() производит значение нуля, поэтому вы можете оставить тройной цикл for. (И, возможно, изменить Int() к 0 в вашем внутреннем repeatedValue: параметра, чтобы сделать его более удобным для чтения.)

Во-вторых, массивы в Swift от копирования при записи, но эта оптимизация может сломаться в многомерных массивов: изменение одного из элементов вложенный массив может привести к перезаписи всего вложенного массива вместо одного элемента. Умножьте, что по глубине вложенных массивов и количеству записей элементов вы продолжаете в двойном цикле for и ... это некрасиво.

Если у вас нет причин, чтобы ваши бункеры были организованы таким образом, я бы рекомендовал найти для них другую структуру данных. Три отдельных массива? Один массив Int, где индекс i красный, i + 1 зеленый, а i + 2 синий? Один массив пользовательского struct, который вы определяете, который имеет отдельные члены r, g и b? Посмотрите, что концептуально соответствует вашим вкусам или остальной части вашего приложения, и профиль, чтобы убедиться, что он работает хорошо.

Наконец, некоторые Swift очки стиля:

  • pixelInfo, r, g и b в вашем втором цикле не меняется. Используйте let, а не var, и оптимизатор поблагодарит вас.
  • Объявление и инициализация чего-то типа let foo: Int = Int(whatever) является излишним. Некоторым людям нравится вводить все свои переменные/константы, но это делает ваш код менее читаемым и сложным для рефакторинга.
  • Int(floor(x)) является избыточным - преобразование в целое число всегда занимает пол.
0

Если у вас есть некоторые проблемы с производительностью в вашем коде, прежде всего, используйте Time Profiler from Instruments. Вы можете запустить его через меню Xcode Build-> Profile, затем открыть приложение «Инструменты», где вы можете выбрать Time Profiler.

Начните запись и сделайте все взаимодействия в своем приложении.

Остановите запись и проанализируйте, где находится «самое узкое» место вашего кода.

Также проверьте параметры «Инвертировать дерево вызовов», «Скрыть отсутствующие символы» и «Скрыть системные библиотеки» для лучшего просмотра результатов профиля.

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