Нахожу в тесте вопросы, которые в принципе не освещаются в лекции. Нужно гуглить на других ресурсах, чтобы решить тест, или же он всё же должен испытывать знания, полученные в ходе лекции? |
Самостоятельная работа 5: Сборка и установка Intel® Integrated Performance Primitives. Использование библиотеки в среде Microsoft® Visual Studio
5.6. Реализация поиска прямых с использованием функций библиотеки Intel® Integrated Performance Primitives
Последовательно рассмотрим шаги реализации функции hough_ipp. Параметры функции в точности соответствуют тем, что были приведены при описании реализации hough _opencv.
Аналогично в начале функции объявим ряд рабочих переменных.
int hough_ipp(const Mat &srcImg, vector<Point> &points1, vector<Point> &points2) { Ipp32f low = 50.0f, high = 100.0f; int minNumPoints = 35, maxLineCount = 40, lineCount, bufSize; Ipp8u* pGraySrc, *pBinSrc, *buffer; IppiSize pGraySize; IppStatus error;
Поскольку детектор ребер Канни работает с полутоновым изображением, то далее исходное изображение конвертируется в оттенки серого посредством вызова функции ippiRGBToGray_8u_C3C1R. Рассмотрим подробнее входные параметры данной функции:
- srcImg.data – указатель на область памяти, содержащую исходное цветное изображение в формате RGB, которое предварительно было выровнено.
- srcImg.step1() – шаг, с которым необходимо выполнять проход от начала массива цветов исходного изображения, чтобы обратиться к элементу следующей строки.
- grayImg.data – указатель на область памяти для сохранения результата конвертирования.
- grayImg.step1() – шаг, с которым необходимо выполнять проход от начала массива интенсивностей пикселей выходного изображения, чтобы обратиться к элементу следующей строки.
- pGraySize – реальные размеры результирующего полутонового изображения.
// создать полутоновое изображение Mat grayImg(srcImg.size(), CV_8UC1); // заполнить размер IppiSize для изображения // в оттенках серого pGraySize.width = srcImg.size().width; pGraySize.height = srcImg.size().height; // преобразовать исходное изображение в оттенки серого error = ippiRGBToGray_8u_C3C1R(srcImg.data, srcImg.step1(), grayImg.data, grayImg.step1(), pGraySize); // проверить результат выполнения // операции преобразования if (error != ippStsNoErr) { printf("ERROR!!! ippiRGBToGray_8u_C3C1R(...)\n\n"); return 1; }
На данном этапе необходимо применить детектор ребер Канни, предварительно вычислив значения вертикального и горизонтального оператора Собеля. Рассмотрим последовательность вызовов функций. Первоначально следует создать матрицу для хранения бинарного изображения, описывающего ребра.
// преобразовать указатель pGraySrc = (Ipp8u *)grayImg.data; // создать бинарное изображение Mat binImg(srcImg.size(), CV_8UC1);
Затем следует определить размеры вспомогательных буферов, которые будут использоваться при вычислении значений горизонтального и вертикального оператора Собеля. Для этого необходимо вызвать функции ippiFilterSobel<type>GetBufferSize_8u16s_C1R (<type> принимает значения Vert или Horiz). Обе функции принимают на вход три параметра: размер матрицы значений оператора Собеля (vertSobelSize, horzSobelSize), размер маски оператора Собеля (ippMskSize3x3), выходной размер вспомогательного буфера (vertSize, horzSize).
// вычислить значения горизонтального и вертикального // оператора Собеля int vertSize, horzSize, vertSobelStep, horzSobelStep; IppiSize vertSobelSize, horzSobelSize; vertSobelSize.width = srcImg.size().width; vertSobelSize.height = srcImg.size().height; horzSobelSize.width = srcImg.size().width; horzSobelSize.height = srcImg.size().height; Ipp16s *horzSobel, *vertSobel; ippiFilterSobelVertGetBufferSize_8u16s_C1R( vertSobelSize, ippMskSize3x3, &vertSize); ippiFilterSobelHorizGetBufferSize_8u16s_C1R( horzSobelSize, ippMskSize3x3, &horzSize); if (vertSize < horzSize) { vertSize = horzSize; }
Аналогично необходимо вычислить размер вспомогательного буфера для детектора Канни посредством вызова функции ippiCannyGetSize, которой на вход передается реальный размер изображения и переменная для записи результирующего размера.
ippiCannyGetSize(pGraySize, &vertSize);
На данном этапе требуется выделить память для значений вертикального и горизонтального оператора Собеля с использованием функции ippiMalloc_16s_C1. Функция принимает в качестве входных параметров ширину и высоту изображения, возвращает указатель на выделенную область памяти и шаг прохода от одной строки к другой.
horzSobel = ippiMalloc_16s_C1(srcImg.size().width, srcImg.size().height, &horzSobelStep); vertSobel = ippiMalloc_16s_C1(srcImg.size().width, srcImg.size().height, &vertSobelStep);
Теперь необходимо выделить память для вспомогательного буфера, который будет использоваться для детектора Канни, посредством вызова функции ippsMalloc_8u.
buffer = ippsMalloc_8u(vertSize);
К настоящему моменту выполнены все подготовительные операции, осталось вычислить значения горизонтального и вертикального оператора Собеля. Соответствующие функции выделены полужирным начертанием. Набор параметров этих функций аналогичен:
- pGraySrc – исходное полутоновое изображение.
- grayImg.step1() – шаг по строке исходного изображения.
- vertSobel / horzSobel – указатель на область памяти, в которую будет записан результат применения оператора Собеля.
- vertSobelStep / horzSobelStep – шаг по строке значений оператора Собеля.
- vertSobelSize / horzSobelSize – размер результирующей матрицы.
- ippMskSize3x3 – размер шаблона оператора Собеля.
- Способ формирования границы для вычисления граничных значений оператора.
- ippBorderConst – граница постоянного размера.
- ippBorderRepl – граница, полученная в результате копирования краевых пикселей.
- ippBorderWrap – граница получена в результате сворачивания изображения в тор.
- ippBorderMirror – зеркальное отображение краевых пикселей.
- ippBorderMirrorR – зеркальные отображение краевых пикселей с копированием.
8. 0 – значение интенсивности на границе в случае, если предыдущий параметр равен ippBorderConst.
9. buffer – указатель на вспомогательный буфер.
error = ippiFilterSobelVertBorder_8u16s_C1R(pGraySrc, grayImg.step1(), vertSobel, vertSobelStep, vertSobelSize, ippMskSize3x3, ippBorderRepl, 0, buffer); if (error != ippStsNoErr) { printf("ERROR!!!\n\n"); return 1; } error = ippiFilterSobelHorizBorder_8u16s_C1R(pGraySrc, grayImg.step1(), horzSobel, horzSobelStep, horzSobelSize, ippMskSize3x3, ippBorderRepl, 0, buffer); if (error != ippStsNoErr) { printf("ERROR!!! \n\n"); return 1; }
Далее необходимо выполнить поиск ребер с помощью детектора Канни, реализованного в библиотеке Intel® IPP. Для этого необходимо вызвать функцию ippiCanny_16s8u_C1R. Функция имеет следующие входные параметры:
- Матрицы значений вертикального (vertSobel) и горизонтального оператора Собеля (horzSobel).
- Шаги по строкам для соответствующих матриц (horzSobelStep, vertSobelStep).
- Указатель на область памяти binImg.data, в которую необходимо записать результирующую матрицу ребер.
- Шаг по строке в результирующей матрице binImg.step1().
- pGraySize – размер матрицы ребер.
- Два пороговых значения (low, high) – параметры детектора Канни.
- Указатель на вспомогательный буфер buffer.
// определить ребра с помощью детектора Канни error = ippiCanny_16s8u_C1R(horzSobel, horzSobelStep, vertSobel, vertSobelStep, binImg.data, binImg.step1(), pGraySize, low, high, buffer); if (error != ippStsNoErr) { printf("ERROR!!! ippiCanny_16s8u_C1R\n\n"); return 1; } // освободить память из-под вспомогательных буферов ippsFree(buffer); ippiFree(horzSobel); ippiFree(vertSobel); namedWindow("Canny (IPP)"); imshow("Canny (IPP)", binImg);
Осталось применить преобразование Хафа, предварительно подготовив необходимый набор параметров. Первоначально требуется определить размер рабочего буфера памяти посредством вызова функции ippiHoughLineGetSize_8u_C1R. Данная функция вычисляет размер буфера bufSize на основании размера исходного изображения pGraySize, значения параметра дискретизации delta полярной системы и заданного максимального количества прямых линий, которые будут детектироваться. Далее следует выделить вспомогательный буфер pBuffer и создать массив lines для хранения продетектированных прямых линий. Теперь можно выполнить преобразование Хафа посредством вызова функции ippiHoughLine_8u32f_C1R.Функция принимает на вход следующий набор параметров:
- pBinSrc – указатель на область памяти, где хранится бинарное изображение ребер.
- binImg.step1() – шаг по строке в бинарном изображении ребер.
- pGraySize – реальный размер результирующей матрицы.
- delta – параметры дискретизации сетки.
- minNumPoints – минимальное количество точек, лежащих на прямой линии.
- lines – указатель на массив линий.
- maxLineCount – максимальное количество линий, которые будет найдено в результате применения преобразования Хафа.
- lineCount – реальное количество продетектированных линий.
- pBuffer – указатель на рабочий буфер памяти.
// преобразовать указатель pBinSrc = (Ipp8u *)binImg.data; // установить параметры для преобразования Хафа IppPointPolar delta; delta.rho = 1; delta.theta = 1.0f * ((float)CV_PI) / 180.0f; IppPointPolar *lines; Ipp8u *pBuffer; // вычислить размер вспомогательного буфера error = ippiHoughLineGetSize_8u_C1R(pGraySize, delta, maxLineCount, &bufSize); if (error != ippStsNoErr) { printf("ERROR!!!\n\n"); return 1; } // выделить память под вспомогательный буфер // и массив линий pBuffer = ippsMalloc_8u(bufSize); lines = (IppPointPolar *)malloc(sizeof(IppPointPolar) * maxLineCount); // выполнить преобразование Хафа error = ippiHoughLine_8u32f_C1R(pBinSrc, binImg.step1(), pGraySize, delta, minNumPoints, lines, maxLineCount, &lineCount, pBuffer); // проверить результат выполнения операции // преобразования if (error != ippStsNoErr) { printf("ERROR!!!\n\n"); return 1; } ippsFree(pBuffer);
Последний шаг, который необходимо выполнить, – преобразовать координаты линий из полярной системы координат в декартову систему, связанную с изображением, и освободить вспомогательную память.
for (int lineIdx = 0; lineIdx < lineCount; lineIdx++) { float rho = lines[lineIdx].rho, theta = lines[lineIdx].theta; Point pt1, pt2; double cosTheta = cos(theta), sinTheta = sin(theta); double x0 = rho * cosTheta, y0 = rho * sinTheta; pt1.x = cvRound(x0 + 1000*(-sinTheta)); pt1.y = cvRound(y0 + 1000*(cosTheta)); pt2.x = cvRound(x0 - 1000*(-sinTheta)); pt2.y = cvRound(y0 - 1000*(cosTheta)); points1.push_back(pt1); points2.push_back(pt2); } binImg.release(); free(lines); return 0; }