Спонсор: Intel
Опубликован: 20.08.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Новосибирский Государственный Университет
Самостоятельная работа 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; 
}   
    
Александра Максимова
Александра Максимова

При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка?
 

Алена Борисова
Алена Борисова

В лекции по обработке полутоновых изображений (http://www.intuit.ru/studies/courses/10621/1105/lecture/17979?page=2) увидела следующий фильтр:


    \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 2 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array} - \frac{1}{9} \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 1 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array}

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

Что вижу я в конструкции фильтра (скорее всего ошибочно): F(x, y) = 2 * I(x, y) - 1/9 I(x, y) = 17/9 * I(x, y), где F(x, y) - яркость отфильтрованного пикселя, а I(x, y) - яркость исходного пикселя с координатами (x, y). Что означает обычное повышение яркости изображения, при этом без учета соседних пикселей (так как их множители равны 0).

Объясните, пожалуйста, как данный фильтр может повышать четкость изображения?

Сергей Кротов
Сергей Кротов
Россия
Дмитрий Донсков
Дмитрий Донсков
Россия, Москва, Московский Авиационный Институт