Палитра и доминантные цвета изображения

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

Пока земляне старательно бухали отмечали наступление Нового Года и провожали Рождество, я решил тоже побухать отдохнуть и развлечься. В качестве упражнения Дима Климкин подкинул одну бессмысленную для нашего проекта, но весьма любопытную в целом задачу: а не можем ли мы получить из картинки её палитру для определения гармонической целесообразности этого изображения. А еще точнее её доминантные цвета. Да не вопрос, подумал я, и между блинами к новогоднему столу и рибаем для перекуса, накидал первую версию анализатора палитры изображения. Посмотрел на результат и понял: что-то не то. Потом накидал второй вариант и оставил оба. Поэтому праздничный пост будет происходить вокруг исследования того как правильно определять палитру изображения в ограниченом цветовом пространстве, как найти доминантные цвета изображения и в чем разница между палитрой и доминантными цветами.

Палитра

По сути под палитрой изображения почти всегда понимают все цвета присутствующие в исходном изображении. В реальной природе мы можем определить если не все многообразие цветовых оттенков, но в разумных и обозримых пределах числовой шкалы значений сигналов цветовых стимулов, уж что-то точно можем. Если мы договоримся, что для моделирования сигнала нам достаточно какой-то точности его описания, то ширина охвата значений этих сигналов и будет палитрой всех доступных нам цветов. Очевидно, что каждое отдельное представления изображения и отображения всех цветов картинки на это пространство будет  некоторым его подмножеством. В цифровой обработке любого сигнала, а изображение это тоже сигнал, под множеством величин мы всегда понимаем его дискретное представление. Таким образом под палитрой изображения в нашем случае можно понимать подмножество всех цветов изображения представленных дискретной картой. По сути мы можем просто взять эти все цвета как-то отиндексировать и каждому целому индексу присвоить конкретное значение в одном из цветовых пространств. Мы будем работать, по традиции с RGB.

Пожалуй, самая большая трудность, с представлением таким образом палитры изображения, состоит в её необозримости человеком. Я, конечно понимаю под всем этим 2016-й год, а не 1990. В те стародавние времена все было просто: все цвета помещаемые в памяти вычислительной машины можно пересчитать на пальцах всех сотрудников отдела обслуживающего один такой комп. Справедливости ради, стоит отметить, иногда хватало пары пальцев одного сисадмина. Да, славные были времена…

Снимок экрана 2016-01-03 в 19.14.04

Но сегодня все плохо по другому, скорее всего, мы уже не справимся с анализом палитры изображения вручную. А иногда все еще не имеет смысла хранить изображение представленного всеми исходными цветами, а сузить их количество до некоторого разумного предела. Так вот такой процесс называется квантизацией цвета: уменьшение количества цветов из всего подмножества, которым может быть представлено изображение до некоторого меньшего. Например, сильно утрируя процесс, 14bit raw цвет представить в 8bit jpeg или 16bit tiff сконвертировать в 8bit png и так далее.

Так вот, очевидным и простым решением для определения основных цветов изображения можно представить себе процесс квантизаци его цветов до какого-то сильно ограниченного набора. Например до 8ми цветов вообще. Или 3-х. И тогда мы как бы будем представлять что-же за основные цвета изображение содержит. И даже сможем их увидеть и запомнить. А еще было бы круто если бы в итоговой картинке цвета были бы гармонизированы, как это было описано у Иттена, или Матюшина. Т.е. выделив доминантные цвета изображения мы сможем сконструировать некий инструмент позволяющий визуализировать «гармоничность»  изображения и, к примеру гармоничность изображения после фильтрации или «синтетической гармонизации». Сегодня сделаем попытку сочинить такой инструмент.

Palette-Source-CubeHist

Median cut

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

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

Я могу сделать предположение, что первая часть этого алгоритма и есть искомый вариант решения задачи: поиск основных или преобладающих цветов изображения. Однако в таком решении сразу можно усмотреть одну проблему: когда количество цветов, на которые нам надо разложить картинку слишком малое, а нам надо всего несколько цветов для анализа. Значит мы найдем не существующие цвета: цвета будут сильно усреднены. Мы конечно можем взять цвет с максимальным бином из каждого такого кубика и обозвать его доминантным, но тогда это уже не будет палитрой. Поэтому оставим этот подход для определения сжатой палитры изображения, которую тоже можно использовать как инструмент визуального анализа изображения в смысле его «гармонической полноценности». А для нахождения преобладающих цветов применим статистический анализ: поиск локальных максимумов в той же самой кубической гистограмме.

Локальные максимумы

Как ни странно, с локальными максимумами оказалось все прекрасно: нашелся код, который можно не только украсть творчески переработать, но и автор оказался прекрасным художником и в целом очень хорошо описал сам алгоритм. Суть реализации состоит в том, что сначала мы собираем статистику картинки в такую же  трехмерную RGB-гистограмму, как и в случае с алгоритмом Median Cut:

color-cube

Каждая ячейка куба будет содержать бины цвета, и сумму всех значений каждого цвета входящего в ячейку. Поскольку размерность гистограммы ограничивается разрешением в 32x32x32 (что бы не плодить лишней сложности вычислений, и в оригинальном варианте 30x30x30), то накопление суммы упрощает расчет среднего цвета ячейки.

color-cube-histogram.jpeg

Локальный максимум ищется полным перебором всего пространства и сравнением с соседними ячейками.

color-cube-histogram-local-maxima

Затем количество локальных максимумов итеративно уменьшаем до искомого количества выкидывая близкие цвета с меньшим весом.   По всем оставшимся локальным максимумам получаем усредненные цвета всех значений попавших в список. Поскольку плотность цвета в локальных максимумах выше, а разница между значениями пикселов в них меньше чем в кубиках из Median Cut, то цвета будут ближе к цветам присутствующим в основном изображении и более адекватно отображать преобладающие цвета исходного изображений.

В этом заключается основное отличие двух моделей: получение «сжатой палитры» и поиска локальных максимумов или доминирующих цветов. Из «сжатой палитры» можно получить новое изображение, которое мы получим отмапив основное и это изображение будет, в целом, содержать тот же баланс цветов, что и основное изображение, но в сильно усеченном виде. Доминирующие цвета описывают лишь состав цветов изображения, которые в основном присутствуют в этом изображении, но из них нельзя получить новое с подходящим балансом цветов.

Реализация

На примере этой задачи покажу как просто, используя еще далеко неполный IMProcessing Framework, сварить готовое приложение для анализа и работы с изображением. Поскольку имплементировать уже существующую функциональность не так интересно, я начал с классов отсутствующих в других движках как класс. К примеру, фреймворк уже умеет читать файлы Adobe .cube с готовыми CLUT, а так же умеет в «режиме реального времени» извлекать 3-х мерную кубическую гистограмму из изображения. В предыдущем посте, я описал общую концепцию работы с фреймворком. Сегодня используя эти общие принципы сконструируем приложение, которое умеет:

  1. Загружать файлы формата  JFIF (jpeg)
  2. «Нормализовать» исходное изображение
  3. Управлять степенью воздействия «нормализатора»
  4. Добавлять к обработке произвольный LUT из файла Adobe формата .cube.
  5. Управлять степенью воздействия CLUT
  6. Показывать линейную гистограмму изображения
  7. Показывать «сжатую палитру» и доминантные цвета изображения и их числовое представление в виде триплета (r,g,b)

В результате конструирования получим что-то вроде такой игрушки:

Palette-Comparsion

Которая наглядно покажет, как простая «нормализация» изображения, к примеру, благотворно влияет на вариативность финальной палитры и перераспределяет доминантные цвета в более вариативный и гармоничный набор.

«Нормализатор»

Пока сильно чудить не будем и соберем фильтр из двух уже готовых:

  1. IMPContrastFilter — позволяет растягивать гистограмму изображения до заданных границ
  2. IMPAutoWBFilter — автокоррекция баланса белого основанная на поиске среднего цвета и коррекции паразитных тонов изображения. По сути это небольшая модификация идеи позаимствованной из блога Андрея Журавлева

import IMProcessing

/// Фильтр изображения
public class IMPTestFilter:IMPFilter {

    /// Будем использовать фильтр управления контрастом через растяжение гистограммы
    var contrastFilter:IMPContrastFilter!

    /// Фильтр автоматического баланса белого
    var awbFilter:IMPAutoWBFilter!

    /// Анализатор линейной гистограммы изображения
    var sourceAnalyzer:IMPHistogramAnalyzer!

    /// Солвер анализатора гистограммы расчета границ светлот изображения
    let rangeSolver = IMPHistogramRangeSolver()

    public required init(context: IMPContext) {
        super.init(context: context)

        //  Инициализируем фильтры в контексте
        contrastFilter = IMPContrastFilter(context: context)
        awbFilter = IMPAutoWBFilter(context: context)

        // Добавляем фильтры в стек
        addFilter(contrastFilter)
        addFilter(awbFilter)

        // Инициализируем анализатор гистограммы
        sourceAnalyzer = IMPHistogramAnalyzer(context: self.context)

        // добавляем к анализатору солвер поиска границ светлот
        sourceAnalyzer.addSolver(rangeSolver)

        // Добавляем к фильтру наблюдающий хендлер к фильтру для
        // для передачи текущего фрейма изображения анализатору
        addSourceObserver { (source) - Void in
            self.sourceAnalyzer.source = source
        }

        // Добавляем к анализатору наблюдающий хендлер обновления расчетов анализа
        sourceAnalyzer.addUpdateObserver({ (histogram) - Void in
            // устанавливаем при каждом изменении изображения границы светлот в контрастном фильтре
            self.contrastFilter.adjustment.minimum = self.rangeSolver.minimum
            self.contrastFilter.adjustment.maximum = self.rangeSolver.maximum
        })

    }
}

Солвер палитры

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

Для нашей цели: вычисления палитры и списка доминирующих цветов используем солвер для расширения анализа IMPHistogramCubeAnalyzer. Результаты вычислений будем отображать в обновляемом NSTableView.

import IMProcessing

///  Типы распределения цветовых акцентов изображения
///
///  - palette:   палитра квантирования цветов изображения.
///               расчитывается по сжеме median-cut преобрзования:
///               http://www.leptonica.com/papers/mediancut.pdf
///  - dominants: расчет доминантных цветов изображения через поиск локальных максимумов
///               функции плотности распределения цветов:
///               https://github.com/pixelogik/ColorCube
///
public enum IMPPaletteType{
    case palette
    case dominants
}

/// Солвер анализатора кубической гистограммы цветов IMPHistogramCubeAnalyzer
public class IMPPaletteSolver: IMPHistogramCubeSolver {

    /// Максимальное количество цветов палитры для анализа
    public var maxColors = Int(8)

    /// Список найденых цветов
    public var colors = [IMPColor]()

    /// Тип палитры
    public var type = IMPPaletteType.dominants

    ///  Хендлер обработчика солвера
    ///
    ///  - parameter analizer:  ссылка на анализатор
    ///  - parameter histogram: кубическая гистограмма изображния
    ///  - parameter imageSize: размер изображения
    public func analizerDidUpdate(analizer: IMPHistogramCubeAnalyzer, histogram: IMPHistogramCube, imageSize: CGSize) {

        var p = [float3]()
        if type == .palette{
            p = histogram.cube.palette(count: maxColors)
        }
        else if type == .dominants {
            p = histogram.cube.dominantColors(count: maxColors)
        }

        colors.removeAll()

        for c in p {
            colors.append(IMPColor(color: float4(rgb: c, a: 1)))
        }
    }
}

Спаиваем все в контроллере представления

В контроллере нам понадобятся основной фильтр приложения, назовем его оригинально: IMPTestFilter, фильтр CLUT: IMPLutFilter, готовый из коробки: IMPHistogramView — для отображения «обычной» гистограммы, IMPHistogramCubeAnalyzer — анализатор кубической гистограммы, к которому присоединим наш солвер IMPPaletteSolver. Наконец, IMPImageView — главное окно показа картинки и общий IMPContext — ключевой класс используемый всеми конструкторами фреймворка. 


class ViewController: NSViewController {

    //
    // Контекст процессинга
    //
    let context = IMPContext()
    //
    // Окно представления загруженной картинки
    //
    var imageView:IMPImageView!

    var pannelScrollView = NSScrollView()

    //
    // Окно вывода гистограммы изображения
    //
    var histogramView:IMPHistogramView!

    //
    // NSTableView - представления списка цветов из палитры
    //
    var paletteView:IMPPaletteListView!

    //
    // Основной фильтр
    //
    var filter:IMPTestFilter!

    //
    // Фильтр CLUT из фалов формата Adobe Cube
    //
    var lutFilter:IMPLutFilter?

    //
    // Анализатор кубической гистограммы изображения в RGB пространстве
    //
    var histograCube:IMPHistogramCubeAnalyzer!

    //
    // Наш солвер для поиска цветов
    //
    var paletteSolver = IMPPaletteSolver()

    var paletteTypeChooser:NSSegmentedControl!

    override func viewDidLoad() {

        super.viewDidLoad()

        configurePannel()

        //
        // Инициализируем кучу нужных нам объектов
        //

        filter = IMPTestFilter(context: context)

        histograCube = IMPHistogramCubeAnalyzer(context: context)
        histograCube.addSolver(paletteSolver)

        imageView = IMPImageView(context: context, frame: view.bounds)
        imageView.filter = filter
        imageView.backgroundColor = IMPColor(color: IMPPrefs.colors.background)

        //
        // Добавляем еще один хендлер к наблюдению за исходной картинкой
        // (еще один был доавлен в основном фильтре IMPTestFilter)
        //
        filter.addSourceObserver { (source) -> Void in
            //
            // для минимизации расчетов анализатор будет сжимать картинку до 1000px по широкой стороне
            //
            if let size = source.texture?.size {
                let scale = 1000/max(size.width,size.height)
                self.histograCube.downScaleFactor = scale.float
            }
        }

        //
        // Добавляем наблюдателя к фильтру для обработки результатов
        // фильтрования
        //
        filter.addDestinationObserver { (destination) -> Void in

            // передаем картинку показывателю кистограммы
            self.histogramView.source = destination

            // передаем результат в анализатор кубической гистограммы
            self.histograCube.source = destination
        }

        //
        // Результаты обновления расчета анализатора выводим в окне списка цветов
        //
        histograCube.addUpdateObserver { (histogram) -> Void in
            self.asyncChanges({ () -> Void in
                self.paletteView.colorList = self.paletteSolver.colors
            })
        }

        view.addSubview(imageView)

....

        IMPDocument.sharedInstance.addDocumentObserver { (file, type) -> Void in
            if type == .Image {
                do{
                    //
                    // Загружаем файл и связываем источником фильтра
                    //
                    self.imageView.source = try IMPImageProvider(context: self.imageView.context, file: file)
                    self.asyncChanges({ () -> Void in
                        self.zoomFit()
                    })
                }
                catch let error as NSError {
                    self.asyncChanges({ () -> Void in
                        let alert = NSAlert(error: error)
                        alert.runModal()
                    })
                }
            }
            else if type == .LUT {
                do {

                    //
                    // Инициализируем дескриптор CLUT
                    //
                    var description = IMPImageProvider.LutDescription()
                    //
                    // Загружаем CLUT
                    //
                    let lutProvider = try IMPImageProvider(context: self.context, cubeFile: file, description: &description)

                    if let lut = self.lutFilter{
                        //
                        // Если CLUT-фильтр добавлен - обновляем его LUT-таблицу из файла с полученным дескриптором
                        //
                        lut.update(lutProvider, description:description)
                    }
                    else{
                        //
                        // Создаем новый фильтр LUT
                        //
                        self.lutFilter = IMPLutFilter(context: self.context, lut: lutProvider, description: description)
                    }

                    //
                    // Добавляем LUT-фильтр, если этот фильтр уже был добавленб ничего не происходит
                    //
                    self.filter.addFilter(self.lutFilter!)
                }
                catch let error as NSError {
                    self.asyncChanges({ () -> Void in
                        let alert = NSAlert(error: error)
                        alert.runModal()
                    })
                }
            }
        }

   ....

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

Полностью проект можно забрать, собрать и опробовать из репозитория проекта ImageMetallingImageMetalling-08. Для правильной сборки локально должна быть установлена мега-библиотека для работы с файлами формата JPEG (JFIF) libjpeg-turbo.  На сегодняшний день это лучшая реализации поддержки этого формата.

Ссылка для скачивания приложения для тех кому даже собраться лень (совместима только с El Capitan и железом после середины 2012г.): ImageMetalling-08.app.

Писол! Всех с благополучно наступившим похмельем!


Авторы блога не преследуют задачи быть предельно корректным, но если заметили явную ашипку, если написали явную глупость, если что-то не понятно: комментируйте или пишите на: imagemetalling [*] gmail.com.


И да, никогда не используйте имплементацию этого формата NSImage/UIImage…

Палитра и доминантные цвета изображения: 32 комментария

  1. Хм, у меня собранное приложенько не взлетело…

    fatal error: *** IMPContext: could not get GPU device…: file /Users/denn/Development/degradr/IMProcessing/IMProcessing/Classes/IMPContext.swift, line 95

    P.S.: Кстати, у меня печально известная серия прошки начала 2011 года с атишной картой, с которой вчера случилось что-то плохое. Я успел забекапить все данные и хотел уже выкидывать комп, но после отчаянного обновления до El Capitan в режиме полосатого экрана по каким-то необяснимым для меня причинам GPU заработал.

    Нравится

    1. Да, забыл предупредить, что будет работать только под EL Capitan, и вполне вероятно на старых прошках не будет. Надо покапать информацию, что в текущем виде Apple поддерживает под Metal на OSX. Насколько я знаю Blizzard еще не сильно стремится поддерживать Metal в своих движках из-за сырости технологии.

      Если не сложно, закиньте инфу по своей железке, буду собирать статистику по совместимости OSX/Metal.

      Нравится

  2. MacBook Pro (15-inch, Early 2011), 2.3 GHz Intel Core i7, 8 GB 1333 MHz DDR3
    Intel HD Graphics 3000 512 MB / AMD Radeon HD 6750M 1024 MB

    Сейчас стоит утилита gfxCardStatus — переключал между встроенной/дискретной — ни там, ни там не запускается.

    Нравится

    1. Да, похоже на то: https://support.apple.com/kb/SP728?locale=en_US . Поддерживаются прошки начиная с середины 2012. С более ранними сложности даже у Nvidia Cuda/OpenCL. Только OpenGL условно надежная штука, в смысле переносимости. Надеюсь Apple доведет до ума слой и расширит совместимость. Но объективности, ради — это весьма нетривиальная задача для ранних GPU. По сути те задачи, которые хочется решать сейчас, на старых карточках было бы проще не решать совсем, а делать все на CPU/DSP и о GPU даже не думать.

      Нравится

      1. А если попытаться обернуть все вокруг Cuda/OpneCL? Все же применение фильтров в режиме реального времени к изображению дает некое дополнительное визуальное ощущение. И ядра переносить достаточно просто между Metal/Cuda/OpenCL. Даже может остановиться на Cuda, поскольку последние релизы под собой скрывают OpenCL.

        Можно провести простой эксперимент с каким-то обычным фильтром.

        Нравится

      2. Мне нужно почитать/обсудить Cuda. Когда NVIDIA выпустила первую карту с ней — у нас в универе она была через месяц — мы не ней пытались запустить рэйтресинг — ничего, естесственно, не получилось (в реальном времени). Тогда еще не было OpenCL. Сейчас, конечно, все поменялось… Я смотрел на ядра OpenCL в darktable — показалось все достаточно просто. Как я в данный момент времени понимаю — OpenCL слой как раз абстрагирует от используемой тихнологии параллельных вычислений — GPU(Nvidia CUDA, что-то AMD)/CPU

        Нравится

      3. В целом, Metal ближе к Cuda. OpenCL местами сильно уступает по стройности идеологии обоим, но у нее неоспоримое преимущество: это полностью переносимая платформа. С точки зрения зрелости и удобства программирования, Cuda, на мой взгляд самая удачная платформа на сегодня.

        Но у Apple, как всегда есть свой взгляд: будущее не обещает нам поддержки ни развития openCl, если только Ати и Интел не вступятся. Cuda очевидно будет поддерживаться nVidia.

        В общем сложный момент определить, что лучше поддерживать. Очевидно, что обернуть все три платформы в итоге можно, можно начать с любой) Я начал с Metal, поскольку теперь это точно родное для эпла, и красиво для программирования.

        Нравится

      4. В идеальном случае так все и будет и в основном все будет работать OK. Но проблема останется с iOS. В iOS не поддерживается ни OpenCL ни Cuda. Только OpenGL и теперь Metal.

        Нравится

      5. Отдельно ядро перенести, возможно, не сложно. Но вклинить другой девайс в текущий дизайн не потребует ли его переработки?

        Нравится

      6. У Metal API есть одно преимущество перед Cuda и OpenCL — это полностью Protocol Oriented Programming. Т.е. за API не стоит конкретный девайс, за апи стоит набор протоколов, которые должен обеспечить слой. Пока сложно сказать насколько вообще все это будет трудоемко, поскольку проект больше не про совместимость, а про некоторое будущее, которое уже наступило и его можно пользовать. Т.е. в итоге-то все эпловские устройства будут работать с Metal. Конечно, с OSX это будет происходить сильно медленнее чем с iOS, но и набор низкоуровневых операций не так велик что-бы встроить OpenCL на слой ниже IMProcessing и прикинуться пртоколами Metal, но со своими ядрами.Думаю вклинить именно OpenCL вполне возможный сценарий. Но я пока сильно в эту сторону не думал.

        Нравится

  3. Ну, собственно, что значит “вклинить” OpenCL? С точки зрения идеологии писать код поддержки старого железа (в рамках одной платформы) — задача бессмысленная. Если говорить о расширении платформ — то речь о вклинивании уже не идет, т.к. нужно просто все переписать с нуля и на перле (с).

    Нравится

    1. Совершенно согласен. Именно по этому, под OSX/iOS не хочется возится ни с Cuda ни с OpenCL. На мой вкус OpenCL немного коряво выглядит, требует много писанины и не всегда оправдывает ожидания в смысле производительности.

      Вот немного замеров: http://arxiv.org/ftp/arxiv/papers/1005/1005.2581.pdf

      Есть еще к стати, OpenMP, тоже вариант.

      Нравится

      1. C OpenMP не очень понятно: поддержка OpenMP 4.0 в GCC/Clang, с какими картами нормально работает. Пока, не смотря на корявость, OpenCL видится наиболее вероятным вариантом.

        Нравится

      2. Не:) Как раз с OpenMP то все понятно, и развивается давно и поддерживается аж c GCC3. И код прозрачный, парадигма стройная, во всех отношениях удачная штука. Но! Это CPU-based расширение, не требующее написание ядер. И карточки GPU по сути ей не нужны. Насколько OpenMP подходит для image processing не могу сказать: не экспериментировал. Но судя по всем сравнениям, иногда OpenMP превосходит OpenCL по производительности просто за счет того, что не занимается компиляцией ядер на лету (чего кстати Cuda и Metal не делают, они работают с уже собранными ядрами). Ну и сами молотилки в железе у OpenMP существенно быстрее любого ядра любой GPU. Т.е. 16 ядер CPU вполне могут уделать 256 GPU. Например туже гистограмму за счет скаттеринга вполне себе можно собрать мадленнее на GPU чем с помощью OpenMP.

        Тут как всегда вопрос компромисса и доступности. На iOS для параллелизации доступны только OpenGL ES и Metal, на OSX доступно почти все, в том числе и OpenMP, поэтому логично выбирать что-то из пересекающегося подмножества и Metal очевидный выбор. А вот на Win/Linux, я бы все же на OpenCL смотрел осторожно. Пока от конкретных реализаций больше негатива, чем позитива. Но OpenMP это конечно совсем другая парадигма. Т.е. тут точно все с нуля писать.

        Или как вариант использовать совместно OpenMP/OpenCL, я очень часто использую DSP для ускорения расчетов там где нельзя ускориться за счет только одного GPU. На самртфонах это дает еще и дополнительный бонус: экономия батарейки.

        Нравится

      3. Согласен, что в своей основе OpenMP — CPU ориентированное решение. Предельно простое и понятное. Но с версии 4.0 можно работать и с GPU (gcc 5, вроде как). Но идеология управления через препроцессорные директивы (в том числе и управление операциями копирования памяти между устройством и хостом) навряд ли позволит написать нормальное с точки зрения архитектуры и расширяемости решение, при этом еще и эффективное. Ну, т.е. цикл на CPU распараллелить это одно, а расширяемую архитектуру с фильтрами/функциями, раскиданную по разным модулям, распараллелить на GPU причем так, чтобы память не гонять туда-сюда (иначе смысла нет) — ну как-то туманно совсем. Но да, рассмотреть стоит, конечно.

        Нравится

      4. Для анализа изображений вполне себе решение, хотя, мне пока больше нравится идея использовать такой флоу: Texture->GPU>ядро с анализом но без скатеринга на gpu->распиленные Structured Data-> и дальше через SIMD инструкции или CBLAS/vDSP сборка и дальнейший анализ. Вместо этого можно использовать OpenMP как более переносимую опцию. vDSP точно не на всех платформах есть. CBLAS не всегда оптимально реализован.

        Нравится

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

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

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

        Нравится

      6. В моем случае выбор практически безальтернативный. Достаточно быстро взлетел тест на OpenCL, поэтому начал работу с ним.
        Угу, пока нахожусь под впечатлением Halide. Немого не понял, что вы имели ввиду под его академичностью. Ну, собственно, да — это ядро процессинга — язык, за которым скрыт реальный API (OpenCL, Metal, GLSL). Набор пользовательских функций это уже вроде бы как другая задача. По идее, код написанный на Halide должен с относительной легкостью взлететь как на маке, как на iOS. Судить об оптимальности генерируемого объектного кода не могу, но, судя по примерам, с этим должно быть все в порядке.

        Нравится

Оставьте комментарий