Опубликован: 02.09.2013 | Доступ: свободный | Студентов: 429 / 54 | Длительность: 19:27:00

Самостоятельная работа 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. Рассмотрим подробнее входные параметры данной функции:

  1. srcImg.data – указатель на область памяти, содержащую исходное цветное изображение в формате RGB, которое предварительно было выровнено.
  2. srcImg.step1() – шаг, с которым необходимо выполнять проход от начала массива цветов исходного изображения, чтобы обратиться к элементу следующей строки.
  3. grayImg.data – указатель на область памяти для сохранения результата конвертирования.
  4. grayImg.step1() – шаг, с которым необходимо выполнять проход от начала массива интенсивностей пикселей выходного изображения, чтобы обратиться к элементу следующей строки.
  5. 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); 
  

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

  1. pGraySrc – исходное полутоновое изображение.
  2. grayImg.step1() – шаг по строке исходного изображения.
  3. vertSobel / horzSobel – указатель на область памяти, в которую будет записан результат применения оператора Собеля.
  4. vertSobelStep / horzSobelStep – шаг по строке значений оператора Собеля.
  5. vertSobelSize / horzSobelSize – размер результирующей матрицы.
  6. ippMskSize3x3 – размер шаблона оператора Собеля.
  7. Способ формирования границы для вычисления граничных значений оператора.
  • 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. Функция имеет следующие входные параметры:

  1. Матрицы значений вертикального (vertSobel) и горизонтального оператора Собеля (horzSobel).
  2. Шаги по строкам для соответствующих матриц (horzSobelStep, vertSobelStep).
  3. Указатель на область памяти binImg.data, в которую необходимо записать результирующую матрицу ребер.
  4. Шаг по строке в результирующей матрице binImg.step1().
  5. pGraySize – размер матрицы ребер.
  6. Два пороговых значения (low, high) – параметры детектора Канни.
  7. Указатель на вспомогательный буфер 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.Функция принимает на вход следующий набор параметров:

  1. pBinSrc – указатель на область памяти, где хранится бинарное изображение ребер.
  2. binImg.step1() – шаг по строке в бинарном изображении ребер.
  3. pGraySize – реальный размер результирующей матрицы.
  4. delta – параметры дискретизации сетки.
  5. minNumPoints – минимальное количество точек, лежащих на прямой линии.
  6. lines – указатель на массив линий.
  7. maxLineCount – максимальное количество линий, которые будет найдено в результате применения преобразования Хафа.
  8. lineCount – реальное количество продетектированных линий.
  9. 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; 
} 
Андрей Терёхин
Андрей Терёхин

Нахожу в тесте вопросы, которые в принципе не освещаются в лекции. Нужно гуглить на других ресурсах, чтобы решить тест, или же он всё же должен испытывать знания, полученные в ходе лекции?

Демянчик Иван
Демянчик Иван

В главе 14 мы видим понятие фильтра, но не могу разобраться, чем он является в теории и практике.

" Искомый объект можно описать с помощью фильтра F= \lbrace f_{x',y'},x' \in \lbrace0, ...,w_f \rbrace , y' \in \lbrace 0,...,h_f \rbrace \rbrace "