Новосибирский Государственный Университет
Опубликован: 20.08.2013 | Доступ: свободный | Студентов: 865 / 38 | Длительность: 14:11: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; 
}   
    
Александра Максимова
Александра Максимова

При прохождении теста 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).

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