Linux-переводы строк или вообще написано в одну строчку (хотя, конечно, самому компилятору ядер всё равно, как разделяются текстовые строки, — он всё обрабатывает правильно). Скорректировать процедуру чтения можно путём изменения режима открытия файла ( "rb" ), тогда даже ядра из файлов с переводами строк в стиле Windows ( 0x0D,0x0A ) будут загружаться в память правильно. Надо сказать, что компилирование ядер «вслепую» (без контроля возникающих ошибок) оправдано лишь в случаях уже «проверенных» ядер. В реальной работе нужно быть точно уверенным, что ядра не содержат синтаксических ошибок и компилируются, а для этого полезно попытаться скомпилировать их отдельно, используя, например, clcc — OpenCL Kernel Compiler ( http://sourceforge.net/projects/clcc/ ), который использует уже установленную на компьютере реализацию OpenCL, а если их несколько — то любую заданную из них. Типичная командная строка компиляции файла с ядром для заданной платформы и заданного устройства выглядит так: clcc -p < Платформа > -d < Устройство > < ИмяФайлаСЯдром > Необходимые для этой строки значения идентификаторов платформы и устройства можно узнать с помощью «справочного» запуска этого компилятора ( clcc -i ). Подсказка по имеющимся возможностям компилятора — clcc -h Если при этом ядрам понадобятся дополнительные параметры, то их использование возможно с помощью опции --cloptions командной строки. Например, в коде ядра reduction.cl с упомянутого выше сайта ( http://www.caam.rice.edu/timwar/HPC12/Examples/OpenCL++/ ) используется величина dx , нигде в файле ядра не определяемая. Она получает своё значение в хост-программе reduction.cpp перед компиляцией ядра и передаётся в рамках опции командной строки при компиляции (например, так: "-Ddx=512" ). Такая возможность компиляции ядер с немного разными значениями подобного типа параметров является для OpenCL вполне оправданной, т.к. разные устройства обладают разными характеристиками. Помимо отдельной компиляции ядер надо обязательно проверять все используемые в OpenCL функции на предмет возможных ошибок при выполнении каждой из них. Это может казаться избыточным, но это лишь отчасти так, и только, если программа компилируется и работает. Если же она — в процессе создания, дополнительные сообщения об ошибках, а также возвращаемые функцией clGetProgramBuildInfo() сведения будут совсем не лишними. 34 Завершить набор советов уместно ещё одним замечанием. В Сети легко обнаружить довольно много примеров, которыми можно воспользоваться для освоения любой незнакомой области, важно не забывать, что программы почти всегда содержат ошибки, и не все эти ошибки обнаружены авторами примеров — просто потому, что у них эти ошибки могли не проявляться. Чаще всего ошибки возникают потому, что пишется программа в одной операционной системе, а проверяется — в другой, при этом должного внимания переносимости кода не уделяется. Например, код из текста достаточно свежей заметки (25 августа 2014) в блоге разработчика (!) библиотек для GPU — фирмы ArrayFire ( http://arrayfire.com/generating-ptx-files-from-opencl-code/) тоже содержит обсуждавшуюся ранее ошибку: .cl-файл открывается для чтения в текстовом режиме ( "r" ). При этом сохраняемый .ptx-файл открывается для записи зачем-то в бинарном режиме ( "wb" ), хотя его содержимое является чисто текстовым. Если фрагменты кода бездумно «вырезать» прямо из заметки, то, как уже говорилось, под Windows ядро просто не откомпилируется, поскольку будет содержать по два символа конца строки, один из которых при считывании файла в текстовом режиме под Windows просто исчезнет. Если же воспользоваться версией кода с GitHub, то наличие ошибки в коде (при проверке даже под Windows) будет незаметно, поскольку ядро там содержит по одному символу конца строки. Справедливости ради надо сказать, что эта ошибка в тексте заметки — не единственная и не главная: обе версии ядра ( CUDA и OpenCL), содержат одинаковую ошибку в операции проверки (неравенство должно быть противоположным!), из-за чего программа, их использующая, не могла бы в принципе работать, но, к счастью, здесь она лишь преобразует ядро из одного вида в другой, а не запускает его... Дополнительные материалы Неплохая статья-введение в программирование с помощью OpenCL (выдержки из неё использовались выше): A Gentle Introduction to OpenCL http://www.drdobbs.com/parallel/a-gentle-introduction-to-opencl/231002854 Ссылки на реализации OpenCL основными изготовителями видеокарт: OpenCL | NVIDIA Developer Zone https://developer.nvidia.com/opencl OpenCL™ Zone | AMD — Develop With AMD http://developer.amd.com/resources/heterogeneous-computing/opencl-zone/ Intel® SDK for OpenCL http://software.intel.com/en-us/articles/intel-opencl-sdk/ Просто интересные ссылки по теме: Differences between VexCL, Thrust, and Boost.Compute http://stackoverflow.com/questions/20154179/differences-between-vexcl-thrust-and-boost-compute Simulation and Rendering of Fire using CUDA https://code.google.com/p/cuda-fire-simulation/ Библиотека ViennaCL: http://sourceforge.net/projects/viennacl/ 35 Библиотека OpenCV (Open Source Computer Vision Library) Общие сведения Это открытая библиотека, содержащая несколько сотен алгоритмов работы с изображениями. Для нас она интересна в первую очередь тем, что некоторые её модули (имеется в виду модуль gpu, «умеющий» использовать карты NVIDIA, а с версии 2.4.3 — также модуль ocl, инициированный фирмой AMD и «задействующий» OpenCL) содержат алгоритмы из различных других модулей и эти алгоритмы используют графическую карту для ускорения работы. Среди остальных модулей (т.е., отдельных статических или динамических библиотек): core (структуры данных, многомерный массив данных Mat , базовые функции, используемые в других модулях), imgproc (обработка изображений: линейная и нелинейная фильтрация, геометрические и цветовые преобразования, гистограммы и т.п), video (видеоанализ: оценка движения, устранение фона, слежение за объектом), objdetect (обнаружение объектов определённых классов, например, лиц, глаз, а также людей, машин), highgui (интерфейс работы с видео и изображениями) и др. Компиляция и сборка проекта с использованием OpenCV Для добавления возможностей OpenCV к проекту, создаваемому с помощью компилятора Visual C++, удобно иметь переменную окружения, скажем, OPENCV , указывающую на каталог, где расположены файлы библиотеки, тогда с помощью $(OPENCV) упрощается указание путей в установках проекта: для компиляции параметр AdditionalIncludeDirectories должен содержать $(OPENCV)\build\include , для сборки программы параметр AdditionalLibraryDirectories должен содержать значение $(OPENCV)\build\ <Платформа> \ <ВерсияСтудии> \lib ) и параметр AdditionalDependencies — список необходимых статических библиотек). В зависимости от сложности программы список может включать такие статические библиотеки: opencv_core <ВерсияOpenCV> .lib , opencv_imgproc <ВерсияOpenCV> .lib , opencv_highgui <ВерсияOpenCV> .lib , opencv_ml <ВерсияOpenCV> .lib , opencv_video <ВерсияOpenCV> .lib , opencv_features2d <ВерсияOpenCV> .lib , opencv_calib3d <ВерсияOpenCV> .lib , opencv_objdetect <ВерсияOpenCV> .lib , opencv_contrib <ВерсияOpenCV> .lib , opencv_legacy <ВерсияOpenCV> .lib , opencv_flann <ВерсияOpenCV> .lib Для работы с графической картой добавляется ещё opencv_gpu <ВерсияOpenCV> .lib Здесь использованы такие обозначения: <Платформа> — это x86 или x64 , <ВерсияСтудии> — vc9 , vc10 , vc11 или vc12 , <ВерсияOpenCV> — три или четыре цифры версии (без точек), например, 2411 для OpenCV v.2.4.11, 230 — для OpenCV v.2.3.0 и т.п. Интересно, что OpenCV v2.3.0 имеет варианты библиотечных файлов только для vc9 и vc10 , а вот версия OpenCV v2.4.8— уже только для vc10 , vc11 и vc12 И, разумеется, для того, чтобы работающая программа могла найти свои динамические библиотеки ( .dll ), нужно добавить путь к ним в переменную окружения Path (или скопировать динамические библиотеки туда, где поиск уже производится, например, в каталог \Windows\system32 ). 8 36
Программирование с использованием OpenCV Имеется два варианта взаимодействия с функциями OpenCV: "старый" (используются заголовочные файлы opencv/ ...) и новый, появившийся со второй версии библиотеки (используются заголовочные файлы opencv2/ ...). Кроме того, в новом варианте мы по- прежнему можем пользоваться функциями в стиле языка C (их названия тогда начинаются с cv... ), либо писать программу на C++. В новом варианте (когда используются заголовочные файлы из каталога opencv2 ) нужно учитывать тот факт, что классы и функции библиотеки помещены в пространство имён cv и для доступа к ним следует применять префикс cv:: (или директиву using namespace cv; ), иногда — для устранения конфликта имён, скажем, с STL — префикс тоже необходим. Работа с динамической памятью в библиотеке не требует явного освобождения памяти, поскольку реализуется подсчёт ссылок на объекты в динамической памяти, но при этом для указателей следует пользоваться шаблонным классом Ptr<> (похож на std::shared_ptr ), т.е., вместо T* ptr = new T(...); лучше писать Ptr ptr = new T(...); тогда указатель на объект будет также иметь и счётчик ссылок на этот объект. Наиболее часто используемым типом величины в OpenCV является массив, определяемый в классе Mat . Значения элементов в таком массиве не совсем произвольны, они могут быть 8- битными (со знаком и без), 16-битными (со знаком и без), 32-битными целыми, вещественными одинарной (32 бита) и двойной (64 бита) точности. Для символических названий этих базовых типов и соответствующих им значений проще всего привести определение соответствующего перечисления из библиотеки: enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; Возможны также многоканальные типы данных, их символические имена имеют ещё символ C и число каналов, например: CV_32FC1 (то же самое, что и CV_32F ), CV_32FC2 (то же самое, что и CV_32FC(2) , CV_8UC(12) и т.п. Самый "ходовой" конструктор объектов — Mat(nrows, ncols, type[,fillValue]) , например, матрица с шестью строками и пятью столбцами, содержащая пару величин (комплексное число) и заполненная значениями 1-2j , создаётся так: Mat M(6,5,CV_32FC2,Scalar(1,-2)); Теперь можно превратить M в 12-канальную 8-битовую матрицу 100x60 (старое содержимое при этом исчезнет): M.create(100,60,CV_8UC(12)); Ещё пример — трёхканальное (цветное) изображение с 1920 столбцами и 1080 строками: Mat img(Size(1920, 1080), CV_8UC3); 37
Обратите внимание, что здесь размеры указаны другим способом, а потому идут в другом порядке! Простейшие примеры программ Первоначальное впечатление о возможностях библиотеки можно получить на простых примерах (сами файлы приведены в приложении: TestCamera1.cpp — вывод видеопотока с камеры в окно программы; Threshold.cpp — преобразование цветного изображения из файла в бинарное чёрно-белое, используются заголовочный файл opencv2/gpu/gpu.hpp и функция cv::gpu::threshold(), работающая с видеокартой через объекты cv::gpu::GpuMat ; LoadAVI.cpp — воспроизведение видеоролика из файла формата AVI). За более подробной информацией по использованию библиотеки следует обращаться к руководству "The OpenCV Reference Manual" (файл opencv23.pdf или новее). Возможно также добавление возможностей OpenCV к программам, написанным на языке Python (с установленным пакетом Numpy!), для чего содержимое каталога библиотеки OpenCV \build\python\2.7\Lib\site-packages (в предположении, что используемый Python — версии 2.7) надо скопировать в каталог Python, именуемый \Lib\site-packages\ (скорее всего, это будет файл cv2.pyd; может быть также и cv.pyd или cv.py). Проверить, что возможности OpenCV доступны в языке Python, теперь можно с помощью одной строчки в интерпретаторе: import cv2. Если всё в порядке, модуль будет подгружен и готов к использованию, в противном случае будет выведено сообщение об ошибке. При благоприятном развитии событий имеет смысл поэкспериментировать с файлом FaceDetect.py: он задействует камеру для получения видеопотока и пытается в этом видеопотоке обнаружить лицо человека, нужно только правильно указать значение переменной haarlocation в этой программе. Следует отметить, что программы с использованием модуля gpu могут быть успешно собраны, но будут ли они они реально использовать GPU, зависит от того, как была создана библиотека OpenCV: с поддержкой CUDA или без неё. Для такой поддержки перед компиляцией библиотека должна быть сконфигурирована с параметром WITH_CUDA=ON При этом, если во время её компиляции CUDA установлена, то компилируется полноценный GPU-модуль. В противном случае модуль тоже создаётся, но во время исполнения вызовы функций из него лишь возбуждают исключительную ситуацию. Появившаяся недавно версия OpenCV 3.0 радикально переработана: библиотека теперь поддерживает "прозрачное" ускорение с помощью OpenCL (т.н. Transparent API). Во время исполнения программы — если OpenCL-реализация наличествует и не запрещена — OpenCL будет использоваться по умолчанию, если алгоритм имеет OpenCL-вариант. Запрещение и разрешение использования OpenCL контролируется специальной переменной окружения OPENCV_OPENCL_RUNTIME С её помощью можно также указать, что нужно использовать конкретное устройство, если их несколько. Обращение к OpenCL-реализации осуществляется при этом динамически, т.е., во время исполнения программы. Существовавшие в версиях 2.4.x отдельные функции и даже пространство имён "ocl" (вместе с типом oclMat) более не используются; появляется унифицированный тип данныхUMat, обслуживающий также и необходимые переносы данных на устройство и из устройства. 38 Графическая библиотека (программный интерфейс) OpenGL Возросшим вычислительным возможностям должны сопутствовать сравнимые изобразительные возможности, иначе трудно убедиться в правильности вычислений и почти невозможно воспользоваться результатами расчётов — из-за возрастающих объёмов перерабатываемой информации. Поэтому понятно, что нужно иметь возможность визуализации исходных и получаемых данных, а для визуального отображения информации необходима какая-нибудь графическая библиотека. Мы ограничимся знакомством только с одной из графических библиотек, но, пожалуй, самой важной и распространённой в настоящее время: кроссплатформенной, доступной из различных языков программирования, — библиотекой (являющейся одновременно и программным интерфейсом) OpenGL ( http://ru.wikipedia.org/wiki/OpenGL). Справедливости ради надо сказать, что воспользоваться этой библиотекой в рамках самой распространённой (пока) операционной системы Windows сложнее всего, поскольку интерфейс OpenGL в рамках Windows "заморозился" на уровне OpenGL версии 1.1, что означает невозможность прямого подключения к более новым функциям и предполагает динамическое подключение к ним (т.е., во время работы программы). Кроме того, тот заголовочный файл, который обычно присутствует в системе, вообще не содержит никакой информации о новых функциях, хотя в файле динамической библиотеки эти функции присутствуют. А это в свою очередь значит, что имеющиеся при компиляторе Visual C++ файлы gl.h и opengl32.lib сами по себе практически бесполезны, и для реальной работы надо использовать также другие заголовки и статические библиотеки, реализующие "надстройку" над OpenGL и/или дополнения к ней. Возможные здесь варианты — это библиотека GLEW ( OpenGL Extension Wrangler, http://glew.sourceforge.net), она создана для облегчения работы с расширениями и различными версиями OpenGL, что актуально для пользователей |