|
Обработка изображений с использованием расширения процессора
Обработка изображений с использованием расширения процессора
ВведениеОбщее представление о SSESIMD-регистры с плавающей точкойТип данных SIMD с плавающей точкой.Модель выполнения SIMDФормат данных в памятиФормат данных SIMD регистра с плавающей точкойSIMD регистр состояния и управленияПоле управления округлениемКоманды Потокового Расширения SIMDОперанды командПример программы с использованием SSEЛистинг программыСкриншот программыВыводСписок литературыВведениеПотоковое расширение SIMD (Streaming SIMD Extensions (Single Instruction, Multiple Data - одна команда, несколько элементов данных)) это обобщающее название всех новых возможностей процессоров, начиная с Pentium III, что созданы для повышения производительности в мультимедиа и информационных приложениях. Эти нововведения (включающие новые регистры, типы данных, и команды) объединяются с выполняемой моделью SIMD для повышения быстродействия приложений. Применение новых SIMD-команд значительно увеличивает производительность приложений, обрабатывающих данные с плавающей запятой, или приложений которые в основном используют алгоритмы с интенсивными вычислениями, выполняя повторяющие операции над большими массивами простых данных. Также от SSE выигрывают приложения, которым нужен постоянный доступ к большим размерам данных.Новые SIMD-команды, реализованные в процессоре Pentium III, увеличивают производительность прикладных программ в следующих областях:· видео· комбинирование графики и видео· обработка изображений· звуковой синтез· распознавание, синтез и компрессия речи· телефония· видео конференции· 2D и 3D графика.В феврале 1999 года Intel представила общественности процессор Pentium III, содержащий обновление технологии MMX, получившей название SSE (Streaming SIMD Extensions -- поточные расширения SIMD). До этого момента инструкции SSE носили имя Katmai New Instructions (KNI), так как первоначально они были включены в процессор Pentium III с кодовым именем Katmai. Процессоры Celeron 533A и выше, созданные на основе ядра Pentium III, тоже поддерживают инструкции SSE. Более ранние версии процессора Pentium II, а также Celeron 533 и ниже (созданные на основе ядра Pentium II) SSE не поддерживают.Инструкции SSE содержат 70 новых команд для работы с графикой и звуком в дополнение к существующим командам MMX. Фактически этот набор инструкций кроме названия KNI имел еще и второе название -- MMX-2. Инструкции SSE позволяют выполнять операции с плавающей запятой, реализуемые в отдельном модуле процессора. В технологиях MMX для этого использовалось стандартное устройство с плавающей запятой.Инструкции SSE2, содержащие 144 дополнительные команды SIMD, были представлены в ноябре 2000 года вместе с процессором Pentium 4. В SSE2 были включены все инструкции предыдущих наборов MMX и SSE.Инструкции SSE3 были представлены в феврале 2004 года вместе с процессором Pentium 4 Prescott; они добавляют 13 команд SIMD, предназначенных для ускорения выполнения сложных математических операций, обработки графики, кодирования видео и синхронизации потоков данных. Инструкции SSE3 также содержат все инструкции MMX, SSE и SSE2.В целом SSE обеспечивает следующие преимущества:· более высокое разрешение/качество при просмотре и обработке графических изображений;· улучшенное качество воспроизведения звуковых и видеофайлов в формате MPEG2, а также одновременное кодирование и декодирование формата MPEG2 в мультимедийных приложениях;· уменьшение загрузки процессора и повышение точности/скорости реагирования при выполнении программного обеспечения для распознавания речи.Инструкции SSE и SSE2 особенно эффективны при декодировании файлов формата MPEG2, который является стандартом сжатия звуковых и видеоданных, используемым в DVD. Следовательно, оснащенные SSE процессоры позволяют достичь максимальной скорости декодирования MPEG2 без использования дополнительных аппаратных средств (например, платы декодера MPEG2). Кроме того, процессоры, содержащие набор инструкций SSE, значительно превосходят предыдущие версии процессоров при распознавании речи.Одним из основных преимуществ SSE по отношению к MMX является поддержка операций SIMD с плавающей запятой, что очень важно при обработке трехмерных графических изображений. Технология SIMD, как и MMX, позволяет выполнять сразу несколько операций при получении процессором одной команды. В частности, SSE поддерживает выполнение до четырех операций с плавающей запятой за цикл; одна инструкция может одновременно обрабатывать четыре блока данных. Для выполнения операций с плавающей запятой инструкции SSE могут использоваться вместе с командами MMX без заметного снижения быстродействия. SSE также поддерживает упреждающую выборку данных (prefetching), которая представляет собой механизм предварительного считывания данных из кэш-памяти (L1, L2).Наилучшие результаты применения новых инструкций процессора обеспечиваются только при их поддержке на уровне используемых приложений. Сегодня большинство компаний, занимающихся разработкой программного обеспечения, модифицировали приложения, связанные с обработкой графики и звука, что позволило в более полной мере использовать возможности SSE. К примеру, графическое приложение Adobe Photoshop поддерживает инструкции SSE, что значительно повышает эффективность использования оснащенных SSE процессоров. Поддержка инструкций SSE встроена в DirectX 6.1 и в самые последние видео и аудио-драйверы, поставляемые с операционными системами Windows 98 Second Edition, Windows Me, Windows NT 4.0 (с пакетом обновления 5 или более поздним), Windows 2000, Windows Vista, Windows 7.Инструкции SSE являются расширением технологий MMX, а SSE2 -- расширением инструкций SSE. Таким образом, процессоры, поддерживающие SSE2, поддерживают также SSE, а процессоры, поддерживающие инструкции SSE, в свою очередь, поддерживают оригинальные команды MMX. Это означает, что стандартные приложения MMX могут выполняться практически на любых системах.Первые процессоры производства AMD с поддержкой расширений SSE3 --это 0,09-микронные версии Athlon 64, а также все версии двухъядерных процессоров Athlon 64 X2.Общее представление о SSESSE обеспечивает инструкции для управления кэшированием всей MMX технологии и 32-битных типов данных. Эти инструкции включают возможность записи данных в память без “засорения” кэша промежуточный буфер с быстрым доступом, и возможность упреждающей выборки кода/данных до их использования.Потоковое Расширение SIMD обеспечивает следующие новые возможности при программировании оборудования IA архитектура Intel (Intel architecture):· Восемь SIMD-регистров с плавающей точкой (XMM0 - XMM7).· Тип данных SIMD (упакованные числа одинарной точности с плавающей точкой) - 128-бит.· Набор команд SSE.SIMD-регистры с плавающей точкойSSE содержит восемь 128-разрядных регистров общего назначения, каждый из них может быть напрямую адресован. Так как эти регистры новые, то для использования нуждаются в поддержке операционной системы.SIMD-регистры с плавающей точкой содержат упакованные 128-разрядные данные. Команды SSE обращается к SIMD-регистрам с плавающей точкой используя регистровые имена XMM0 и до XMM7 . SIMD-регистры с плавающей точкой могут быть использованы для вычислений над данными; но не могут быть использованы для адресации памяти. Адресация выполняется с помощью определенных в IA режимов адресации и регистров общего назначения (EAX, EBX, ECX, EDX, EBP, ESI, EDI и ESP).Также вводится новый регистр контроля/состояния MXCSR, он используется для маскирования и демаскирования обработки числовых исключительных ситуаций, для определения режима округления, для установки режима сброса в ноль, и для просмотра состояния флагов.Если произошло исчезновение порядка (underflow), а поле flush-to-zero (FZ бит 15) регистра MXCSR установлено в 1, то процессор выполняет следующие действия: · возвращает нулевое значение в качестве результата, присваивая ему знак истинного результата· устанавливает в 1 биты 4 и 5 регистра MXCSR (флаги обнаружения исключений исчезновения порядка и неточного результата).Указанные действия выполняются в том случае, если исключение underflow маскировано (бит 11 регистра MXCSR установлен в 1). При таком режиме увеличивается скорость работы программ, в которых часто происходит исчезновение порядка результата. Достигается это, однако, ценой снижения точности вычислений. Указанный режим работы не является согласованным с требованиями стандарта IEEE 754MMX регистры физически реализованы на стандартных для архитектуры Intel 80-разрядных регистрах данных с плавающей точкой. То есть, переход от операций MMX к операциям с плавающей точкой требует запуска команды EMMS. Но так как SIMD-регистры с плавающей точкой являются отдельным регистровым файлом, то команды MMX и команды с плавающей точкой могут быть смешаны с командами SSE без выполнение специальных инструкций таких как EMMS.Тип данных SIMD с плавающей точкойОсновной тип данных SSE это 128-разрядное значение, содержащее четыре последовательно расположенных (“упакованных”) 32-разрядных числа одинарной точности с плавающей точкой (single-precision floating-point (SPFP)) Каждое 32-разрядное число с плавающей точкой имеет 1 знаковый бит, 8 битов порядка и 23 бита мантиссы, что соответствует стандарту IEEE-754 Определяет формат хранения мантиссы, экспоненты и знака, форматы положительного и отрицательного нуля, плюс и минус бесконечностей, а также определение «не числа» (NaN), методы, обработку ситуаций на формат представления чисел одинарной точности с плавающей запятой (SPFP_чисел). Данный формат сохранен и в процессорах начиная с Pentium III, однако для упакованных чисел с плавающей точкой используется представление в 32-разрядном формате с одинарной точностью. Поэтому в отдельных случаях результаты вычислений с плавающей точкой в архитектуре х87 могут отличаться от результатов таких же вычислений, использующих новые SSE SPFP-команды.Новые команды SIMD над целыми могут работать над типами данных состоящих из упакованных байт, слов или двойных слов. Новые команды предварительной выборки работают над данными размер которых от 32 байт и выше.Команды SSE копирует упакованные типы данных (данные одинарной точности с плавающей точкой - двойные слова) в и из памяти в 64-битные или 128-битные блоки. Однако, при вычислении арифметических или логических операций над упакованными данными, SSE работает параллельно над каждым двойным словом заключенным в SIMD регистре с плавающей точкой.Новые SIMD-команды над целыми подчиняются соглашениям принятых в инструкциях MMX и работают над данными в MMX регистрах, а не в 128-разрядных SIMD регистрах с плавающей точкой.Модель выполнения SIMDТак как потоковое расширение SSE поддерживает операции над упакованными типами данных одинарной точности с плавающей точкой, и дополнительные SIMD команды над целыми, поддерживаются операции над упакованными типами данных (байт, слово или двойное слово).Этот подход был выбран потому что большинство приложений обработки мультимедиа имеют следующие характеристики: · существенна параллельность; · широкий динамический уровень, отсюда базированы на переменных с плавающей точкой; · регулярное и повторяющая выборка шаблонов из памяти; · локализированные повторяющие операции выполняемые над данными; · независимый процесс управления данными. Потоковое Расширение SIMD доступно из всех режимов выполнения: Защищенного режима (Protected mode), реально адресуемого режима (Real-address mode), и виртуального режима 8086 (Virtual 8086 mode). Формат данных в памятиВ SSE вводиться новый упакованный 128-разрядный тип данных который состоит из четырех чисел одинарной точности с плавающей точкой. Бит 0 это наименьше значащий - бит (LSB), и бит 127 это наибольше значащий - бит (MSB). Байты в новом формате данных имеют последовательные адреса памяти. Порядок как всегда немного странный, то есть байты с меньшими адресами имеют меньшее значение чем байты с старшими адресамиФормат данных SIMD регистра с плавающей точкойЗначение в SIMD регистрах с плавающей точкой имеет тот же формат, что и 128_разрядные величины в памяти. Есть два режима доступа к памяти: 128-битный и 32-битный. Таблица 2-1 показывает точность и диапазон типа данных. Кодируется только дробная часть мантиссы. Бит целого будет 1 для всех чисел, за исключением 0 и ненормированного ограниченного числа. Показатель типа данных с обычной точностью кодируется в смещенный формат. Таблица 2-1. Точность и диапазон SSE типа данных с плавающей точкой|
Тип Данных | Длина | Точность (в битах) | Приблизительный диапазон нормированного числа | | | | | Двоичное | Десятичное | | С одинарной точностью | 32 | 24 | от 2-126 до 2-127 | от 1.18*10-38 до 1.70*1038 | | | Таблица 2-2 показывает кодирование для всех классов действительных чисел (то есть, ноль, ненормированное ограниченное число, нормированное ограниченное число, и ?) и NaN Not a Number - не число для типа данных с одинарной точностью. Она также показывает формат для вещественных неопределенных значений, то есть QNaN Quiet NaN - "тихий NaN" и SNaN Signalling NaN - "сигнализирующий NaN" которые используются некоторыми функциями SSE для активизации обработчика исключений.При сохранении вещественных значений в памяти, значения с одинарной точностью сохраняются в памяти в 4 последовательных байтах. 128-битный режим доступа используется для 128-разрядной выборки из памяти, 128-разрядной пересылки между SSE регистрами с плавающей запятой и всеми логическими, распаковки и арифметическими командами. 32-битный режим доступа используется для 32_разрядной выборки из памяти, 32-разрядной пересылки между SSE регистрами с плавающей запятой и скалярными арифметическими командами.Таблица 2-2. Вещественные числа и кодирование NaNSIMD регистр состояния и управленияРегистр состояния и управления используется для установки флагов обнаружения арифметических исключений, флагов режимов обработки арифметических исключений, режима округления, режима flush-to-zero и для просмотра флага состояния. Содержимое этого регистра может быть загруженно с помощью инструкций LDMXCSR и FXRSTOR и сохранено в памяти с помощью инструкций STMXCSR и FXSAVE. Биты 0-5 (поле обнаружения исключений) содержат 6 флагов, которые служат признаками детектирования арифметических SIMD исключений с плавающей точкой (0-нет, 1-да). Исключение произойдет только после следующей команды SSE. Потоковое расширение SSE использует только один флаг исключения для каждой исключительной ситуации. Здесь не предоставляется возможность для уведомления об индивидуальных исключительных ситуациях внутри упакованных данных. В ситуации, когда происходит несколько идентичных исключительных ситуаций в одной инструкции, соответствующий флаг исключения обновляется и указывает, что хотя бы одно из этих условий произошло. По умолчанию эти флаги сбрасываются.Биты 7-12 (поле маскирования исключений) определяют, как обрабатываются обнаруженные исключения. Если флаг установлен, то соответствующее исключение маскировано и обрабатывается процессором, который формирует приемлемый результат (в соответствии с процедурой, установленной по умолчанию) и продолжает выполнение программы. Если флаг сброшен, то вызывается программный обработчик для этого исключения. По умолчанию флаги устанавливаются в 1, значит что все исключения маскированы.Биты 13-14 (RC) устанавливают режим округления результатов при выполнении SSE-команд над данными с плавающей точкой. По умолчанию устанавливается режим округление до ближайшего.Бит 15 (FZ) используется для включения режима “Flush To Zero”. По умолчанию бит 15 установлен в 0, что выключает режим “Flush To Zero”.Остальные биты регистра MXCSR (биты 16-31 и бит 6) определены как зарезервированные и установлены в 0; попытка записи не нулевых значений в эти биты, используя инструкцию FXRSTOR или LDMXCSR, вызовет исключение общего нарушения защиты (general protection exception).Поле управления округлениемПоле управления округлением (RC) регистра MXCSR (биты 13 и 14) управляют как округляется результат инструкции с плавающей точкой.Поддерживается четыре режима округления:· округление до ближайшего· до меньшего или равного· до большего или равного· и в сторону нуля (смотреть таблицу 2-3).Округление до ближайшего - режим по умолчанию и он подходит для большинства приложений. Он обеспечивает наиболее точный и статистически несмещенную оценку правильного результата.Таблица 2-3. Поле управления округлением (RC)|
Режим округления | Установка полей RC | Описание | | Округление до ближайшего | 00B | Результатом округления берется наилучшее приближение до точного результата. Если два значения одинаково близки к точному результату, то берется четное значение (то есть, то значение у которого наименьший значащий разряд установлен в ноль) | | Округление до меньшего или равного (в сторону -?) | 01B | Результат округления ближайшее, но не больше чем точное решение. | | Округление до большего или равного (в сторону +?) | 10B | Результат округления ближайшее, но не меньше чем точное решение. | | Округление в сторону нуля (усечение) | 11B | Результат округления ближайшее, но не больше чем абсолютное значение точного решения. | | | Команды Потокового Расширения SIMDПотоковое Расширение SIMD состоит из 70 команд, сгруппированных в следующие категории:· Команды копирования данных· Арифметические команды· Команды сравнения · Команды преобразования типов данных· Логические команды· Дополнительные целочисленные SIMD-команды· Команды перестановки· Команды управления состоянием· Команды управления кэшированиемОперанды командПараллельные операции, как правило, действуют одновременно на все четыре 32-разрядных элемента данных в каждом из 128-разрядных операндов В именах команд, выполняющих параллельные операции, присутствует суффикс ps. Например, команда addps складывает 4 пары элементов данных и записывает полученные 4 суммы в соответствующие элементы первого операнда. Скалярные операции действуют на младшие (занимающие разряды 0-31) элементы данных двух операндов Остальные три элемента данных в выходном операнде не изменяются (исключение составляет команда скалярного копирования movss). В имени команд, выполняющих скалярные операции, присутствует суффикс ss (например, команда addss). Большинство команд имеют два операнда. Данные, содержащиеся в первом операнде, могут использоваться командой, а после ее выполнения, как правило, замещаются результатами. Данные во втором операнде используются в команде и после ее выполнения не изменяются. Далее в тексте входным называется второй операнд, а выходным - первый.Для всех команд адрес операнда в памяти должен быть выровнен по 16-байтной границе, кроме не выровненных команд сохранения и загрузки.Пример программы с использованием SSEПрограмма выполняет изменение значения цветовых составляющих каждого пикселя картинки (загружаемой с жесткого диска) для применения эффекта размытия.1. Изображение загружается (посредством диалогового окна) в компоненту «TImage».2. (после выбора пунктов «операции - Размытие Г.») Проверяется на соответствие формату 24 бита на пиксель.3. В специальном диалоговом окне, вводится опции (радиус зерна размытия), и запускается обработка изображения.4. Рассчитывается зерно размытия картинки по установленным параметрам, где производится расчет (списка весов) в несколько этапов.5. выделяется память для обработки изображения попиксельно, а также для обработки строк.7. копируется изображение в память ЭВМ.8. построчно производим эффект гауссово размытия к цветовым составляющим каждого пикселя.9. теперь каждую колонку с помощью созданного списка весов создаем эффект размытия.10. обработанные данные записываются в результативный компонент «TImage».11. освобождается выделенная память для скопированного изображения и обработки строк.12. (по выбору пункта «операции - сохранить» на вкладке «результат») данные результативного изображения сохраняются в файл.Листинг программыconstMaxKernelSize = 64;delay_names = 'миллисекунд';//for imagePRGBTriple = ^TPxlC;TPxlC = record//TPxlCb:byte;g:byte;r:byte;end;PRow = ^TRow; //массив картинкиTRow = array[0..1000000] of TPxlC;PPRows = ^TPRows; //массив строки пикселейTPRows = array[0..1000000] of PRow;TKernelSize = 1..MaxKernelSize;TKernel = record //зерноSize: TKernelSize; //размер зернаWeights: array[-(MaxKernelSize-1)..MaxKernelSize] of single;end;TXMMSingle = array[0..3] of Single;//массив для SSETXMMArrByte = array[0..15] of byte;//массив пикселейTXMMRsByte = recorditem:TXMMArrByte;end;TSSERegLines = array[0..5] of TXMMRsByte;//основная процелура размытияprocedure GBlur(theBitmap: TBitmap; radius: double; withSSE:boolean); varfrm_img: Tfrm_img;implementationuses DateUtils, optscopyimg, optsblurimg;{$R *.dfm}constMAX_imageSize = 65535;//построение зерна (списка весов) размытия (без SSE)//MakeGaussianKernel noSSE-----------------------------------------------------procedure MakeGaussianKernel(var K: TKernel; radius: double;MaxData, DataGranularity: double);//Делаем K (гауссово зерно) со среднеквадратичным отклонением = radius.//Для текущего приложения мы устанавливаем переменные MaxData = 255,//DataGranularity = 1. Теперь в процедуре установим значение//K.Size так, что при использовании K мы будем игнорировать Weights (вес)//с наименее возможными значениями. (Малый размер нам на пользу,//поскольку время выполнения напрямую зависит от//значения K.Size.)varj: integer;temp, delta: double;KernelSize: TKernelSize;a,b:smallint;begin//получили строку весов (зерна)for j:=Low(K.Weights) to High(K.Weights) do begintemp := j / radius;K.Weights[j] := exp(-(temp * temp) / 2);end;//делаем так, чтобы sum(Weights) = 1:temp:=0;for j := Low(K.Weights) to High(K.Weights) dotemp := temp + K.Weights[j];//все сумировалиfor j := Low(K.Weights) to High(K.Weights) doK.Weights[j] := K.Weights[j] / temp;//делим каждое на сумму (нормирование)//теперь отбрасываем (или делаем отметку "игнорировать"//для переменной Size) данные, имеющие относительно небольшое значение -//это важно, в противном случае смазавание происходим с малым радиусом и//той области, которая "захватывается" большим радиусом...KernelSize := MaxKernelSize;delta := DataGranularity / (2 * MaxData);temp := 0;while (temp < delta) and (KernelSize > 1) dobegintemp := temp + 2 * K.Weights[KernelSize];dec(KernelSize);end;//выравниваниеK.Size := KernelSize;//теперь для корректности возвращаемого результата проводим ту же//операцию с K.Size, так, чтобы сумма всех данных была равна единице:temp := 0;for j := -K.Size to K.Size dotemp := temp + K.Weights[j];//for j := -K.Size to K.Size doK.Weights[j] := K.Weights[j] / temp;//end;//построение зерна (списка весов) размытия с SSE//MakeGaussianKernel SSE-------------------------------------------------------procedure MakeGaussianKernelSSE(var K: TKernel; radius: double;MaxData, DataGranularity: double);//Делаем K (гауссово зерно) со среднеквадратичным отклонением = radius.//Для текущего приложения мы устанавливаем переменные MaxData = 255,//DataGranularity = 1. Теперь в процедуре установим значение//K.Size так, что при использовании K мы будем игнорировать Weights (вес)//с наименее возможными значениями. (Малый размер нам на пользу,//поскольку время выполнения напрямую зависит от//значения K.Size.)constnmax=3;varj: integer;temp, delta: double;KernelSize: TKernelSize;xmm_n,xmm_r,xmm_a:TXMMSingle;_low,_high,na:smallint;begin_low:=Low(K.Weights);_high:=High(K.Weights);j:=_low;for na:=0 to nmax do xmm_a[na]:=2;//константа 2for na:=0 to nmax do xmm_r[na]:=radius;//радиусasmpush eaxpush ebxpush ecxpush edxmovups xmm0,xmm_a//2 в SSEmovups xmm1,xmm_r//радиус в SSEend;while (j<=_high) do beginfor na:=0 to nmax doif ((j+na)<=_high) thenxmm_n[na]:=j+naelse break;//копирование простое и передача не дает оптимизации в SSEasmmovups xmm2,xmm_n //jdivps xmm2,xmm1 //j/radiusmovups xmm_n,xmm2mulps xmm2,xmm2 //temp^2movups xmm_n,xmm2divps xmm2,xmm0 //temp*temp/2movups xmm_n,xmm2end;//asmfor na:=0 to nmax do beginif (j<=_high) thenK.Weights[j]:=exp(-xmm_n[na])else break;inc(j);end;//forend;//while//получили строку весов (зерна)//делаем так, чтобы sum(Weights) = 1:temp:=0;for j := Low(K.Weights) to High(K.Weights) dotemp := temp + K.Weights[j];//все сумировалиfor j := Low(K.Weights) to High(K.Weights) doK.Weights[j] := K.Weights[j] / temp;//делим каждое на сумму (нормирование)for na:=0 to nmax do xmm_n[na]:=temp;asmmovups xmm0,xmm_n;end;j:=_low;while (j<=_high) do beginfor na:=0 to nmax do beginif ((j+na)<=_high) thenxmm_n[na]:=K.Weights[j+na]else break;end;//forasmmovups xmm1,xmm_ndivps xmm1,xmm0//K.Weights[j]/tempmovups xmm_n,xmm1end;for na:=0 to nmax do beginif (j<=_high) thenK.Weights[j]:=xmm_n[na]else break; inc(j);end;end;//while//отбрасываем (или делаем отметку "игнорировать"//для переменной Size) данные, имеющие относительно небольшое значение -//это важно, в противном случае смазавание происходим с малым радиусом и//той области, которая "захватывается" большим радиусом...KernelSize := MaxKernelSize;delta := DataGranularity / (2 * MaxData);temp := 0;while (temp < delta) and (KernelSize > 1) dobegintemp := temp + 2 * K.Weights[KernelSize];dec(KernelSize);end;//выравниваниеK.Size := KernelSize;//для корректности возвращаемого результата проводим ту же//операцию с K.Size, так, чтобы сумма всех данных была равна единице:temp := 0;for j := -K.Size to K.Size dotemp := temp + K.Weights[j];for na:=0 to nmax do xmm_n[na]:=temp;asmmovups xmm0,xmm_n;end;j:=_low;while (j<=_high) do beginfor na:=0 to nmax do beginif ((j+na)<=_high) thenxmm_n[na]:=K.Weights[j+na]else break;end;//forasmmovups xmm1,xmm_ndivps xmm1,xmm0//K.Weights[j]/tempmovups xmm_n,xmm1end;for na:=0 to nmax do beginif (j<=_high) thenK.Weights[j]:=xmm_n[na]else break; inc(j);end;end;//whileasmpop edxpop ecxpop ebxpop eaxend;end;//TrimInt - округление по указаным границам Integerfunction TrimInt(Lower, Upper, theInteger: integer): integer;beginif (theInteger <= Upper) and (theInteger >= Lower) thenresult := theIntegerelse if theInteger > Upper thenresult := Upperelseresult := Lower;end;//TrimReal - округление по указанным рамкам Realfunction TrimReal(Lower, Upper: integer; x: double): integer;beginif (x < upper) and (x >= lower) thenresult := trunc(x)else if x > Upper thenresult := Upperelseresult := Lower;end;//BlurRow - размытие строки без SSEprocedure BlurRow(var theRow: array of TPxlC; K: TKernel; P: PRow);varj, n: integer;tr, tg, tb: double; //tempRed и др.w: double;beginfor j := 0 to High(theRow) dobegintb := 0;tg := 0;tr := 0;for n := -K.Size to K.Size dobeginw := K.Weights[n];//TrimInt задает отступ от края строки...with theRow[TrimInt(0, High(theRow), j - n)] dobegintb := tb + w * b;tg := tg + w * g;tr := tr + w * r;end;//withend;//forwith P[j] dobeginb := TrimReal(0, 255, tb);g := TrimReal(0, 255, tg);r := TrimReal(0, 255, tr);end;end; Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TPxlC));end;//GBlur - полное размытие картинкиprocedure GBlur(theBitmap: TBitmap; radius: double; withSSE:boolean);varRow, Col: integer;theRows: PPRows;K: TKernel;ACol: PRow;P: PRow;beginif (theBitmap.HandleType <> bmDIB) or (theBitmap.PixelFormat <> pf24Bit) thenraiseexception.Create('GBlur может работать только с 24-битными изображениями');if (withSSE) then MakeGaussianKernelSSE(K, radius, 255, 1)else MakeGaussianKernel(K, radius, 255, 1);GetMem(theRows, theBitmap.Height * SizeOf(PRow));GetMm(ACol, theBitmap.Height * SizeOf(TPxlC));frm_img.img_pbar.Max:=theBitmap.Height+theBitmap.Width+4;//запись позиции данных изображения:for Row := 0 to theBitmap.Height - 1 dotheRows[Row] := theBitmap.Scanline[Row];//размываем каждую строчку:P := AllocMem(theBitmap.Width * SizeOf(TPxlC));if (frm_imgbluropts.CheckBox1.Checked) then beginfor Row := 0 to theBitmap.Height - 1 do beginBlurRow(Slice(theRows[Row]^, theBitmap.Width), K, P);frm_img.img_pbar.StepBy(1);end;end;//теперь размываем каждую колонкуReAllocMem(P, theBitmap.Height * SizeOf(TPxlC));if (frm_imgbluropts.CheckBox2.Checked) then beginfor Col := 0 to theBitmap.Width - 1 dobegin//- считываем первую колонку в TRow:frm_img.img_pbar.StepBy(1);for Row := 0 to theBitmap.Height - 1 doACol[Row] := theRows[Row][Col];BlurRow(Slice(ACol^, theBitmap.Height), K, P);//теперь помещаем обработанный столбец на свое место в данные изображения:for Row := 0 to theBitmap.Height - 1 dotheRows[Row][Col] := ACol[Row];end;end; FreeMem(theRows);FreeMem(ACol);ReAllocMem(P, 0);frm_img.img_pbar.Max:=0;end;//end blur--------------------------------------------------------------------- //открыть картинкуprocedure Tfrm_img.act_srcOpenImageExecute(Sender: TObject);beginif (img_OpenPictureDialog.Execute) then beginimg_src.Picture.LoadFromFile(img_OpenPictureDialog.FileName);img_lblImageSizeV.Caption:=format('%d - %d',[img_src.Picture.Width,img_src.Picture.Height]);img_log.Lines.Add(format('open file "%s"',[img_OpenPictureDialog.FileName]));img_log.Lines.Add(format('image width="%d" height="%d"',[img_src.Picture.Width,img_src.Picture.Height]));end;end;//по высоте картинку - источникprocedure Tfrm_img.act_srcProportionalImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_src.Proportional:=Checked;end;end;//по высоте картинку - результатprocedure Tfrm_img.act_desProportionalImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_des.Proportional:=Checked;end;end;//копировать - цветовое копирование картинки с умножением на выбранный цветprocedure Tfrm_img.act_srcCopyExecute(Sender: TObject);constxcount=16;varmx,nx,ny,nw,nh:word;citm:^TPxlC;axmm:TSSERegLines;xmm_0:TXMMArrByte;nn,xn:byte;np1,np2,np3:byte;ncolor:tcolor;xc:array[0..3] of byte;timebefore:Cardinal;beginif (frm_optsimgcopy.ShowModal=mrYes) then begintimebefore:=MilliSecondOfTheHour(Now);if (img_src.Picture.Width > MAX_imageSize) or (img_src.Picture.Height > MAX_imageSize) then beginMessageDlg(img_errmsg[0].Text,mtError,[mbok],0);end else beginnw:=img_src.Picture.Width;//n sizenh:=img_src.Picture.Height;img_des.Picture.Bitmap.Width:=nw;//set n sizeimg_des.Picture.Bitmap.Height:=nh;img_pbar.Max:=nh+1;//set progressbarncolor:=frm_optsimgcopy.Shape1.Brush.Color;np1:=frm_optsimgcopy.ComboBox1.ItemIndex;np2:=frm_optsimgcopy.ComboBox2.ItemIndex;np3:=frm_optsimgcopy.ComboBox3.ItemIndex;for xn:=0 to 4 do beginxmm_0[xn*3+0]:=GetBValue(ncolor);//bluexmm_0[xn*3+1]:=GetGValue(ncolor);//greenxmm_0[xn*3+2]:=GetRValue(ncolor);//redend;asmpush eaxpush ebxpush ecxpush edxmovups xmm1,xmm_0end;for ny:=0 to (nh-1) do begincitm:=img_src.Picture.Bitmap.ScanLine[ny];nx:=0;while (nx<=nw) do beginFillChar(xmm_0,16,0);//clearfor nn:=0 to 4 do beginif ((nx+nn)<=nw) then beginxmm_0[nn*3+0]:=citm.b;xmm_0[nn*3+1]:=citm.g;xmm_0[nn*3+2]:=citm.r;end else break;//ifinc(citm);end;//forasm//write,make,readmovups xmm0,xmm_0andps xmm0,xmm1//multiply color'smovups xmm_0,xmm0end;//asmfor nn:=0 to 4 do beginif (nx<=nw) thenimg_des.Canvas.Pixels[nx,ny]:=rgb(xmm_0[nn*3+np3],xmm_0[nn*3+np2],xmm_0[nn*3+np1])else break;inc(nx); end;//forend;//while...img_pbar.StepBy(1);end;//for...asmpop edxpop ecxpop ebxpop eaxend;end;//if...img_pbar.Max:=0;timebefore:=MilliSecondOfTheHour(Now)-timebefore;Label1.Caption:=format('%d %s',[timebefore,delay_names]);img_log.Lines.Add(format('make action="copy image" at="%d" milliseconds',[timebefore]));end;end;//инициализация операцийprocedure Tfrm_img.FormCreate(Sender: TObject);beginimg_errmsg[0]:=tstringlist.create;//error msgimg_errmsg[0].Add('Изображение слишком большое.');//err maxsize imageimg_errmsg[0].Add(format('Максимальный размер не должен превышать %d.',[MAX_imageSize]));img_errmsg[0].Add('Попробуйте выбрать другое.');img_tabs.ActivePage:=img_tab1;//pageLabel1.Caption:=format('0 %s',[delay_names]);end;//уборка мусораprocedure Tfrm_img.FormDestroy(Sender: TObject);beginimg_errmsg[0].Free;end;//Гауссово размытие с применением расширения SSEprocedure Tfrm_img.act_effBlurGauseExecute(Sender: TObject);varb: TBitmap;fticks:Cardinal;beginif (frm_imgbluropts.ShowModal=mrYes) then beginfticks:=MilliSecondOfTheDay(Now);img_des.Picture.LoadFromFile(img_OpenPictureDialog.FileName);GBlur(img_des.Picture.Bitmap,frm_imgbluropts.ComboBox1.ItemIndex,True);fticks:=MilliSecondOfTheDay(Now)-fticks;img_log.lines.add(format('make action="copy blur SSE" at="%d" milliseconds',[fticks]));Label1.Caption:=format('%d %s',[fticks,delay_names]);end;end;//сохранить результатprocedure Tfrm_img.act_desSaveImageExecute(Sender: TObject);beginif (img_SavePictureDialog.Execute) then beginimg_des.Picture.SaveToFile(img_SavePictureDialog.FileName);end;end;//центрировать результатprocedure Tfrm_img.act_desCenterImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_des.Center:=Checked;end;end;//растянуть результатprocedure Tfrm_img.act_desStrechImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_des.Stretch:=Checked;end;end;//центрировать источникprocedure Tfrm_img.act_srcCenterImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_src.Center:=Checked;end;end;//растянуть источникprocedure Tfrm_img.act_srcStrechImgExecute(Sender: TObject);beginwith (sender as taction) do beginimg_src.Stretch:=Checked;end;end;//Гауссово размытие без SSE (простое)procedure Tfrm_img.act_effBlurGauseNoSSEExecute(Sender: TObject);varb: TBitmap;fticks:Cardinal;beginif (frm_imgbluropts.ShowModal=mrYes) then beginfticks:=MilliSecondOfTheDay(Now);img_des.Picture.LoadFromFile(img_OpenPictureDialog.FileName);GBlur(img_des.Picture.Bitmap,frm_imgbluropts.ComboBox1.ItemIndex,False);fticks:=MilliSecondOfTheDay(Now)-fticks;img_log.lines.add(format('make action="copy blur" at="%d" milliseconds',[fticks]));Label1.Caption:=format('%d %s',[fticks,delay_names]);end;end;Скриншот программыРисунок 3-1 вкладка "источник" Рисунок 3-2 вкладка "результат" ВыводСравним производительность при использовании оптимизации кода приложения под расширение SIMD процессора SSE и CPU. Тест производился на процессоре Intel® Core™2 Duo CPU T8300 2,4Ghz с поддержкой MMX,SSE-SSE4, EM64T.Таблица 4-1|
Сравнение времени обработки изображения 800х800 | | № | время обработки с SSE, мс | время обработки на ЦП, мс | коэфициент ускорения | отклонение от среднего | | 1 | 840 | 1 032 | 1,2286 | 0,0071 | | 2 | 841 | 1 047 | 1,2449 | 0,0093 | | 3 | 832 | 1 033 | 1,2416 | 0,0059 | | 4 | 839 | 1 028 | 1,2253 | 0,0104 | | 5 | 836 | 1 035 | 1,2380 | 0,0024 | | | Таблица 4-2|
Сравнение времени обработки изображения 1024х768 | | № | время обработки с SSE, мс | время обработки на ЦП, мс | коэфициент ускорения | отклонение от среднего | | 1 | 1 589 | 1 940 | 1,2209 | 0,0331 | | 2 | 1 529 | 1 955 | 1,2786 | 0,0246 | | 3 | 1 560 | 1 956 | 1,2538 | 0,0002 | | 4 | 1 551 | 1 954 | 1,2598 | 0,0058 | | 5 | 1 545 | 1 942 | 1,2570 | 0,0029 | | | Таблица 4-3|
Сравнение времени обработки изображения 1600х1200 | | № | время обработки с SSE, мс | время обработки на ЦП, мс | коэфициент ускорения | отклонение от среднего | | 1 | 2 369 | 3 037 | 1,2820 | 0,0195 | | 2 | 2 403 | 3 021 | 1,2572 | 0,0053 | | 3 | 2 406 | 3 005 | 1,2490 | 0,0135 | | 4 | 2 389 | 2 989 | 1,2512 | 0,0113 | | 5 | 2 374 | 3 022 | 1,2730 | 0,0105 | | | Таблица 4-4|
Сравнение времени обработки изображения 2560х1600 | | № | время обработки с SSE, мс | время обработки на ЦП, мс | коэфициент ускорения | отклонение от среднего | | 1 | 5 054 | 6 332 | 1,2529 | 0,0062 | | 2 | 5 058 | 6 365 | 1,2584 | 0,0007 | | 3 | 5 050 | 6 376 | 1,2626 | 0,0035 | | 4 | 5 024 | 6 321 | 1,2582 | 0,0009 | | 5 | 4 968 | 6 277 | 1,2635 | 0,0044 | | |
|
|