Методы наиболее успешной практики для Использования vImage

Эта глава дает инструкции для получения оптимальной производительности от vImage. Это покрывает следующие методы наиболее успешной практики:

Загрузка данных изображения

Первый шаг к интеграции vImage в Ваши собственные приложения загружал необработанные данные изображения в память. Можно использовать Изображение платформа I/O для загрузки изображений любого главного формата файла образа (.JPG.PNG.GIF) в буферы C-стиля (недействительный * массивы).

Ниже пример того, как извлечь необработанные данные изображения из локального файла. Если требуется знать больше о других методах и опциях, доступных в Изображении I/O, посмотрите Изображение Руководство по программированию I/O.

NSURL* url = [NSURL fileURLWithPath:filename];
//Create the image source with the options left null for now
//Keep in mind since we created it, we're responsible for getting rid of it
CGImageSourceRef image_source = CGImageSourceCreateWithURL( (CFURLRef)url, NULL);
if(image_source == NULL)
{
    //Something went wrong
    fprintf(stderr, "VImage error: Couldn't create image source from URL\n");
    return false;
}
//Now that we got the source, let's create an image from the first image in the CGImageSource
CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
 
//We created our image, and that's all we needed the source for, so let's release it
CFRelease(image_source);
 
if(image == NULL)
{
    //something went wrong
    fprintf(stderr, "VImage error: Couldn't create image source from URL\n");
    return false;
}

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

Так как vImage обрабатывает только обработку изображений, необходимо обратиться к другой технологии для фактического отображения изображения. В зависимости от цели Вашего приложения или среды приложения Вы использовали (Углерод или Какао), Вам, вероятно, придется найти способ вывести на экран результирующие пиксельные данные (Кварц, например), или сохранить данные изображения на диск (Изображение I/O).

Используйте планарные форматы изображения

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

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

Используйте в своих интересах мозаики

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

В целом, vImage функции имеют намного лучшую производительность, когда данные, которые они обрабатывают (включая буферы ввода и вывода) помещаются в кэши данных процессора. К данным, хранившим в кэшах CPU, можно получить доступ намного быстрее, чем данные, хранившие в оперативной памяти. В то время как кэш-память CPU быстра, она также ограничивается в пространстве. Размер кэша варьируется с процессора на процессор, но в целом хорошо сохранить размеры мозаики изображения ниже 2 МБ для процессоров Intel и ниже 512 КБ для процессоров PowerPC.

  • Вот некоторые подсказки для мозаичного размещения:

    • Некоторые кэши могут только содержать мелкую сумму за один раз (обычно 512 КБ или меньше)

      • Размеры мозаики 128 КБ - 256 КБ дают в целом лучшую пропускную способность

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

    • Для квадратных мозаик размер мозаики от 128 КБ до 256 КБ дает лучшую полную пропускную способность.

    • Который функционирует, мозаика или мультипоток внутренне подвержены изменениям от одного выпуска до другого. При выполнении собственного мозаичного размещения или многопоточности можно, вероятно, улучшить производительность при помощи kvImageDoNotTile флаг со всеми функциями. Вам, возможно, также понадобится kvImageDoNotTile отметьте, если Вы обеспокоены vImage, блокирующим на условной переменной, в то время как рабочие потоки выполняют работу. Это может потребоваться, чтобы предотвращать смену приоритетов, в режиме реального времени кодируют.

    • Лучший размер мозаики варьируется согласно функции. Функции с очень небольшим вычислением на байт (главным образом функции преобразования) являются самыми быстрыми с мозаиками, меньшими, чем 16 КБ. Типичные функции vImage прилагают все усилия с мозаиками на 256 КБ. Размер мозаики менее важен для тяжелых вычислением функций (таких как те, которые используют многопоточность).

Выровняйте буферы данных

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

Здесь некоторые подсказки о выравнивании данных и буферизуют размеры:

  • Хотя vImage терпит меньшее выравнивание для лучшей производительности, все должно составить выровненных 16 байтов и rowbytes должно быть кратное число 16 байтов.

  • Данные с плавающей точкой должны составить выровненные по крайней мере 4 байта, или некоторые функции перестанут работать.

  • Значение Вы передаете в rowBytes параметр к функции не должен быть питанием 2.

Буферы повторного использования

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

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

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

Каждая функция, использующая временный буфер, имеет a src и a dest параметр (оба из типа vImage_Buffer). Функция использует только height и width поля этих параметров; data и rowBytes поля проигнорированы.

Если возможно, приложения должны также попытаться снова использовать регулярные буферы изображения что data поле vImage_Buffer тип данных указывает на. Это экономит время, иначе потратил перераспределение и заполнение нулями буфера.

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

Распараллельте соответственно

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

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

В OS X v10.4 и позже, некоторые функции vImage прозрачно многопоточны внутренне. Они делают свою собственную проверку параметра и только мультираспараллеливают в случаях, где ожидается, что будет получен выигрыш в производительности. vImage поддерживает свой собственный лениво выделенный пул потоков для выполнения этой работы. Таким образом Ваш код должен просто автоматически быть многопоточным без Вас делающий что-либо для тех функций vImage, которые были внутренне многопоточны. (В OS X v10.4.0, это большинство функций в Geometry.h, и гамма функциональность.) Потоки не уничтожаются когда-то создаваемые. Они снова используются. Вызывающий поток может блокировать, в то время как он ожидает вторичных потоков для окончания их работы. Безопасно вызвать внутренне многопоточные функции повторно используемо.

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

Разделите 2D ядра в 1D ядра

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

Можно, конечно, передать 2D ядро одному из vImageConvolve функции. vImage использует 2D ядро для выполнения девять, умножаются, и восемь добавляют операции для вычислений каждого пиксельного результата. Для получения лучшей производительности вызовите vImageConvolve функционируйте дважды, как только для каждого из 1D скручивают фильтры. Когда разделено, vImage выполняет три, умножаются, и два добавляют, что операции на пиксель на скручивают передачу, поскольку в общей сложности шесть умножаются, и четыре добавляют операции. Вы заметите, что, разделяя ядро свертки на многократные передачи, существует алгоритмические сбережения одной трети умножить операций и половины добавить операций. Когда ядра разделяются, для M x N ядро, стоимость обработки примерно сокращена от M*N до M+N. Для больших ядер сбережения становятся более существенными. 5 x 5 ядер могли бы быть в 2.5 раза быстрее, когда разделено, и 11x11 ядро, могли бы быть более чем в 5 раз быстрее!

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

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

Разделение фильтров могло бы быть медленнее в подпрограммах, где стоимость пересечения изображения выше, чем включенная арифметика. Это обычно происходит с очень большими изображениями или изображениями, не вписывающимися в физический RAM.